ftp -o - https://jcs.org/move_in | sh -
at master 2665 lines 79 kB view raw
1" vim-plug: Vim plugin manager 2" ============================ 3" 4" Download plug.vim and put it in ~/.vim/autoload 5" 6" curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ 7" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim 8" 9" Edit your .vimrc 10" 11" call plug#begin('~/.vim/plugged') 12" 13" " Make sure you use single quotes 14" 15" " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align 16" Plug 'junegunn/vim-easy-align' 17" 18" " Any valid git URL is allowed 19" Plug 'https://github.com/junegunn/vim-github-dashboard.git' 20" 21" " Multiple Plug commands can be written in a single line using | separators 22" Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets' 23" 24" " On-demand loading 25" Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' } 26" Plug 'tpope/vim-fireplace', { 'for': 'clojure' } 27" 28" " Using a non-master branch 29" Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' } 30" 31" " Using a tagged release; wildcard allowed (requires git 1.9.2 or above) 32" Plug 'fatih/vim-go', { 'tag': '*' } 33" 34" " Plugin options 35" Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' } 36" 37" " Plugin outside ~/.vim/plugged with post-update hook 38" Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } 39" 40" " Unmanaged plugin (manually installed and updated) 41" Plug '~/my-prototype-plugin' 42" 43" " Initialize plugin system 44" call plug#end() 45" 46" Then reload .vimrc and :PlugInstall to install plugins. 47" 48" Plug options: 49" 50"| Option | Description | 51"| ----------------------- | ------------------------------------------------ | 52"| `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use | 53"| `rtp` | Subdirectory that contains Vim plugin | 54"| `dir` | Custom directory for the plugin | 55"| `as` | Use different name for the plugin | 56"| `do` | Post-update hook (string or funcref) | 57"| `on` | On-demand loading: Commands or `<Plug>`-mappings | 58"| `for` | On-demand loading: File types | 59"| `frozen` | Do not update unless explicitly specified | 60" 61" More information: https://github.com/junegunn/vim-plug 62" 63" 64" Copyright (c) 2017 Junegunn Choi 65" 66" MIT License 67" 68" Permission is hereby granted, free of charge, to any person obtaining 69" a copy of this software and associated documentation files (the 70" "Software"), to deal in the Software without restriction, including 71" without limitation the rights to use, copy, modify, merge, publish, 72" distribute, sublicense, and/or sell copies of the Software, and to 73" permit persons to whom the Software is furnished to do so, subject to 74" the following conditions: 75" 76" The above copyright notice and this permission notice shall be 77" included in all copies or substantial portions of the Software. 78" 79" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 80" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 81" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 82" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 83" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 84" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 85" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 86 87if exists('g:loaded_plug') 88 finish 89endif 90let g:loaded_plug = 1 91 92let s:cpo_save = &cpo 93set cpo&vim 94 95let s:plug_src = 'https://github.com/junegunn/vim-plug.git' 96let s:plug_tab = get(s:, 'plug_tab', -1) 97let s:plug_buf = get(s:, 'plug_buf', -1) 98let s:mac_gui = has('gui_macvim') && has('gui_running') 99let s:is_win = has('win32') 100let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win) 101let s:vim8 = has('patch-8.0.0039') && exists('*job_start') 102if s:is_win && &shellslash 103 set noshellslash 104 let s:me = resolve(expand('<sfile>:p')) 105 set shellslash 106else 107 let s:me = resolve(expand('<sfile>:p')) 108endif 109let s:base_spec = { 'branch': 'master', 'frozen': 0 } 110let s:TYPE = { 111\ 'string': type(''), 112\ 'list': type([]), 113\ 'dict': type({}), 114\ 'funcref': type(function('call')) 115\ } 116let s:loaded = get(s:, 'loaded', {}) 117let s:triggers = get(s:, 'triggers', {}) 118 119if s:is_win 120 function! s:plug_call(fn, ...) 121 let shellslash = &shellslash 122 try 123 set noshellslash 124 return call(a:fn, a:000) 125 finally 126 let &shellslash = shellslash 127 endtry 128 endfunction 129else 130 function! s:plug_call(fn, ...) 131 return call(a:fn, a:000) 132 endfunction 133endif 134 135function! s:plug_getcwd() 136 return s:plug_call('getcwd') 137endfunction 138 139function! s:plug_fnamemodify(fname, mods) 140 return s:plug_call('fnamemodify', a:fname, a:mods) 141endfunction 142 143function! s:plug_expand(fmt) 144 return s:plug_call('expand', a:fmt, 1) 145endfunction 146 147function! s:plug_tempname() 148 return s:plug_call('tempname') 149endfunction 150 151function! plug#begin(...) 152 if a:0 > 0 153 let s:plug_home_org = a:1 154 let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p')) 155 elseif exists('g:plug_home') 156 let home = s:path(g:plug_home) 157 elseif !empty(&rtp) 158 let home = s:path(split(&rtp, ',')[0]) . '/plugged' 159 else 160 return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') 161 endif 162 if s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_fnamemodify(home, ':h') ==# s:first_rtp 163 return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.') 164 endif 165 166 let g:plug_home = home 167 let g:plugs = {} 168 let g:plugs_order = [] 169 let s:triggers = {} 170 171 call s:define_commands() 172 return 1 173endfunction 174 175function! s:define_commands() 176 command! -nargs=+ -bar Plug call plug#(<args>) 177 if !executable('git') 178 return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.') 179 endif 180 if has('win32') 181 \ && &shellslash 182 \ && (&shell =~# 'cmd\.exe' || &shell =~# 'powershell\.exe') 183 return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.') 184 endif 185 if !has('nvim') 186 \ && (has('win32') || has('win32unix')) 187 \ && !has('multi_byte') 188 return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.') 189 endif 190 command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(<bang>0, [<f-args>]) 191 command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(<bang>0, [<f-args>]) 192 command! -nargs=0 -bar -bang PlugClean call s:clean(<bang>0) 193 command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source' s:esc(s:me) | endif 194 command! -nargs=0 -bar PlugStatus call s:status() 195 command! -nargs=0 -bar PlugDiff call s:diff() 196 command! -nargs=? -bar -bang -complete=file PlugSnapshot call s:snapshot(<bang>0, <f-args>) 197endfunction 198 199function! s:to_a(v) 200 return type(a:v) == s:TYPE.list ? a:v : [a:v] 201endfunction 202 203function! s:to_s(v) 204 return type(a:v) == s:TYPE.string ? a:v : join(a:v, "\n") . "\n" 205endfunction 206 207function! s:glob(from, pattern) 208 return s:lines(globpath(a:from, a:pattern)) 209endfunction 210 211function! s:source(from, ...) 212 let found = 0 213 for pattern in a:000 214 for vim in s:glob(a:from, pattern) 215 execute 'source' s:esc(vim) 216 let found = 1 217 endfor 218 endfor 219 return found 220endfunction 221 222function! s:assoc(dict, key, val) 223 let a:dict[a:key] = add(get(a:dict, a:key, []), a:val) 224endfunction 225 226function! s:ask(message, ...) 227 call inputsave() 228 echohl WarningMsg 229 let answer = input(a:message.(a:0 ? ' (y/N/a) ' : ' (y/N) ')) 230 echohl None 231 call inputrestore() 232 echo "\r" 233 return (a:0 && answer =~? '^a') ? 2 : (answer =~? '^y') ? 1 : 0 234endfunction 235 236function! s:ask_no_interrupt(...) 237 try 238 return call('s:ask', a:000) 239 catch 240 return 0 241 endtry 242endfunction 243 244function! s:lazy(plug, opt) 245 return has_key(a:plug, a:opt) && 246 \ (empty(s:to_a(a:plug[a:opt])) || 247 \ !isdirectory(a:plug.dir) || 248 \ len(s:glob(s:rtp(a:plug), 'plugin')) || 249 \ len(s:glob(s:rtp(a:plug), 'after/plugin'))) 250endfunction 251 252function! plug#end() 253 if !exists('g:plugs') 254 return s:err('plug#end() called without calling plug#begin() first') 255 endif 256 257 if exists('#PlugLOD') 258 augroup PlugLOD 259 autocmd! 260 augroup END 261 augroup! PlugLOD 262 endif 263 let lod = { 'ft': {}, 'map': {}, 'cmd': {} } 264 265 if exists('g:did_load_filetypes') 266 filetype off 267 endif 268 for name in g:plugs_order 269 if !has_key(g:plugs, name) 270 continue 271 endif 272 let plug = g:plugs[name] 273 if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for') 274 let s:loaded[name] = 1 275 continue 276 endif 277 278 if has_key(plug, 'on') 279 let s:triggers[name] = { 'map': [], 'cmd': [] } 280 for cmd in s:to_a(plug.on) 281 if cmd =~? '^<Plug>.\+' 282 if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i')) 283 call s:assoc(lod.map, cmd, name) 284 endif 285 call add(s:triggers[name].map, cmd) 286 elseif cmd =~# '^[A-Z]' 287 let cmd = substitute(cmd, '!*$', '', '') 288 if exists(':'.cmd) != 2 289 call s:assoc(lod.cmd, cmd, name) 290 endif 291 call add(s:triggers[name].cmd, cmd) 292 else 293 call s:err('Invalid `on` option: '.cmd. 294 \ '. Should start with an uppercase letter or `<Plug>`.') 295 endif 296 endfor 297 endif 298 299 if has_key(plug, 'for') 300 let types = s:to_a(plug.for) 301 if !empty(types) 302 augroup filetypedetect 303 call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim') 304 augroup END 305 endif 306 for type in types 307 call s:assoc(lod.ft, type, name) 308 endfor 309 endif 310 endfor 311 312 for [cmd, names] in items(lod.cmd) 313 execute printf( 314 \ 'command! -nargs=* -range -bang -complete=file %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, %s)', 315 \ cmd, string(cmd), string(names)) 316 endfor 317 318 for [map, names] in items(lod.map) 319 for [mode, map_prefix, key_prefix] in 320 \ [['i', '<C-O>', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']] 321 execute printf( 322 \ '%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, %s, "%s")<CR>', 323 \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix) 324 endfor 325 endfor 326 327 for [ft, names] in items(lod.ft) 328 augroup PlugLOD 329 execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s)', 330 \ ft, string(ft), string(names)) 331 augroup END 332 endfor 333 334 call s:reorg_rtp() 335 filetype plugin indent on 336 if has('vim_starting') 337 if has('syntax') && !exists('g:syntax_on') 338 syntax enable 339 end 340 else 341 call s:reload_plugins() 342 endif 343endfunction 344 345function! s:loaded_names() 346 return filter(copy(g:plugs_order), 'get(s:loaded, v:val, 0)') 347endfunction 348 349function! s:load_plugin(spec) 350 call s:source(s:rtp(a:spec), 'plugin/**/*.vim', 'after/plugin/**/*.vim') 351endfunction 352 353function! s:reload_plugins() 354 for name in s:loaded_names() 355 call s:load_plugin(g:plugs[name]) 356 endfor 357endfunction 358 359function! s:trim(str) 360 return substitute(a:str, '[\/]\+$', '', '') 361endfunction 362 363function! s:version_requirement(val, min) 364 for idx in range(0, len(a:min) - 1) 365 let v = get(a:val, idx, 0) 366 if v < a:min[idx] | return 0 367 elseif v > a:min[idx] | return 1 368 endif 369 endfor 370 return 1 371endfunction 372 373function! s:git_version_requirement(...) 374 if !exists('s:git_version') 375 let s:git_version = map(split(split(s:system(['git', '--version']))[2], '\.'), 'str2nr(v:val)') 376 endif 377 return s:version_requirement(s:git_version, a:000) 378endfunction 379 380function! s:progress_opt(base) 381 return a:base && !s:is_win && 382 \ s:git_version_requirement(1, 7, 1) ? '--progress' : '' 383endfunction 384 385function! s:rtp(spec) 386 return s:path(a:spec.dir . get(a:spec, 'rtp', '')) 387endfunction 388 389if s:is_win 390 function! s:path(path) 391 return s:trim(substitute(a:path, '/', '\', 'g')) 392 endfunction 393 394 function! s:dirpath(path) 395 return s:path(a:path) . '\' 396 endfunction 397 398 function! s:is_local_plug(repo) 399 return a:repo =~? '^[a-z]:\|^[%~]' 400 endfunction 401 402 " Copied from fzf 403 function! s:wrap_cmds(cmds) 404 let cmds = [ 405 \ '@echo off', 406 \ 'setlocal enabledelayedexpansion'] 407 \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds]) 408 \ + ['endlocal'] 409 if has('iconv') 410 if !exists('s:codepage') 411 let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0) 412 endif 413 return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage)) 414 endif 415 return map(cmds, 'v:val."\r"') 416 endfunction 417 418 function! s:batchfile(cmd) 419 let batchfile = s:plug_tempname().'.bat' 420 call writefile(s:wrap_cmds(a:cmd), batchfile) 421 let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0}) 422 if &shell =~# 'powershell\.exe' 423 let cmd = '& ' . cmd 424 endif 425 return [batchfile, cmd] 426 endfunction 427else 428 function! s:path(path) 429 return s:trim(a:path) 430 endfunction 431 432 function! s:dirpath(path) 433 return substitute(a:path, '[/\\]*$', '/', '') 434 endfunction 435 436 function! s:is_local_plug(repo) 437 return a:repo[0] =~ '[/$~]' 438 endfunction 439endif 440 441function! s:err(msg) 442 echohl ErrorMsg 443 echom '[vim-plug] '.a:msg 444 echohl None 445endfunction 446 447function! s:warn(cmd, msg) 448 echohl WarningMsg 449 execute a:cmd 'a:msg' 450 echohl None 451endfunction 452 453function! s:esc(path) 454 return escape(a:path, ' ') 455endfunction 456 457function! s:escrtp(path) 458 return escape(a:path, ' ,') 459endfunction 460 461function! s:remove_rtp() 462 for name in s:loaded_names() 463 let rtp = s:rtp(g:plugs[name]) 464 execute 'set rtp-='.s:escrtp(rtp) 465 let after = globpath(rtp, 'after') 466 if isdirectory(after) 467 execute 'set rtp-='.s:escrtp(after) 468 endif 469 endfor 470endfunction 471 472function! s:reorg_rtp() 473 if !empty(s:first_rtp) 474 execute 'set rtp-='.s:first_rtp 475 execute 'set rtp-='.s:last_rtp 476 endif 477 478 " &rtp is modified from outside 479 if exists('s:prtp') && s:prtp !=# &rtp 480 call s:remove_rtp() 481 unlet! s:middle 482 endif 483 484 let s:middle = get(s:, 'middle', &rtp) 485 let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])') 486 let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)') 487 let rtp = join(map(rtps, 'escape(v:val, ",")'), ',') 488 \ . ','.s:middle.',' 489 \ . join(map(afters, 'escape(v:val, ",")'), ',') 490 let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g') 491 let s:prtp = &rtp 492 493 if !empty(s:first_rtp) 494 execute 'set rtp^='.s:first_rtp 495 execute 'set rtp+='.s:last_rtp 496 endif 497endfunction 498 499function! s:doautocmd(...) 500 if exists('#'.join(a:000, '#')) 501 execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '<nomodeline>' : '') join(a:000) 502 endif 503endfunction 504 505function! s:dobufread(names) 506 for name in a:names 507 let path = s:rtp(g:plugs[name]) 508 for dir in ['ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin'] 509 if len(finddir(dir, path)) 510 if exists('#BufRead') 511 doautocmd BufRead 512 endif 513 return 514 endif 515 endfor 516 endfor 517endfunction 518 519function! plug#load(...) 520 if a:0 == 0 521 return s:err('Argument missing: plugin name(s) required') 522 endif 523 if !exists('g:plugs') 524 return s:err('plug#begin was not called') 525 endif 526 let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000 527 let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)') 528 if !empty(unknowns) 529 let s = len(unknowns) > 1 ? 's' : '' 530 return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', '))) 531 end 532 let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)') 533 if !empty(unloaded) 534 for name in unloaded 535 call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) 536 endfor 537 call s:dobufread(unloaded) 538 return 1 539 end 540 return 0 541endfunction 542 543function! s:remove_triggers(name) 544 if !has_key(s:triggers, a:name) 545 return 546 endif 547 for cmd in s:triggers[a:name].cmd 548 execute 'silent! delc' cmd 549 endfor 550 for map in s:triggers[a:name].map 551 execute 'silent! unmap' map 552 execute 'silent! iunmap' map 553 endfor 554 call remove(s:triggers, a:name) 555endfunction 556 557function! s:lod(names, types, ...) 558 for name in a:names 559 call s:remove_triggers(name) 560 let s:loaded[name] = 1 561 endfor 562 call s:reorg_rtp() 563 564 for name in a:names 565 let rtp = s:rtp(g:plugs[name]) 566 for dir in a:types 567 call s:source(rtp, dir.'/**/*.vim') 568 endfor 569 if a:0 570 if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2)) 571 execute 'runtime' a:1 572 endif 573 call s:source(rtp, a:2) 574 endif 575 call s:doautocmd('User', name) 576 endfor 577endfunction 578 579function! s:lod_ft(pat, names) 580 let syn = 'syntax/'.a:pat.'.vim' 581 call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn) 582 execute 'autocmd! PlugLOD FileType' a:pat 583 call s:doautocmd('filetypeplugin', 'FileType') 584 call s:doautocmd('filetypeindent', 'FileType') 585endfunction 586 587function! s:lod_cmd(cmd, bang, l1, l2, args, names) 588 call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) 589 call s:dobufread(a:names) 590 execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args) 591endfunction 592 593function! s:lod_map(map, names, with_prefix, prefix) 594 call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) 595 call s:dobufread(a:names) 596 let extra = '' 597 while 1 598 let c = getchar(0) 599 if c == 0 600 break 601 endif 602 let extra .= nr2char(c) 603 endwhile 604 605 if a:with_prefix 606 let prefix = v:count ? v:count : '' 607 let prefix .= '"'.v:register.a:prefix 608 if mode(1) == 'no' 609 if v:operator == 'c' 610 let prefix = "\<esc>" . prefix 611 endif 612 let prefix .= v:operator 613 endif 614 call feedkeys(prefix, 'n') 615 endif 616 call feedkeys(substitute(a:map, '^<Plug>', "\<Plug>", '') . extra) 617endfunction 618 619function! plug#(repo, ...) 620 if a:0 > 1 621 return s:err('Invalid number of arguments (1..2)') 622 endif 623 624 try 625 let repo = s:trim(a:repo) 626 let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec 627 let name = get(opts, 'as', s:plug_fnamemodify(repo, ':t:s?\.git$??')) 628 let spec = extend(s:infer_properties(name, repo), opts) 629 if !has_key(g:plugs, name) 630 call add(g:plugs_order, name) 631 endif 632 let g:plugs[name] = spec 633 let s:loaded[name] = get(s:loaded, name, 0) 634 catch 635 return s:err(v:exception) 636 endtry 637endfunction 638 639function! s:parse_options(arg) 640 let opts = copy(s:base_spec) 641 let type = type(a:arg) 642 if type == s:TYPE.string 643 let opts.tag = a:arg 644 elseif type == s:TYPE.dict 645 call extend(opts, a:arg) 646 if has_key(opts, 'dir') 647 let opts.dir = s:dirpath(s:plug_expand(opts.dir)) 648 endif 649 else 650 throw 'Invalid argument type (expected: string or dictionary)' 651 endif 652 return opts 653endfunction 654 655function! s:infer_properties(name, repo) 656 let repo = a:repo 657 if s:is_local_plug(repo) 658 return { 'dir': s:dirpath(s:plug_expand(repo)) } 659 else 660 if repo =~ ':' 661 let uri = repo 662 else 663 if repo !~ '/' 664 throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo) 665 endif 666 let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') 667 let uri = printf(fmt, repo) 668 endif 669 return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri } 670 endif 671endfunction 672 673function! s:install(force, names) 674 call s:update_impl(0, a:force, a:names) 675endfunction 676 677function! s:update(force, names) 678 call s:update_impl(1, a:force, a:names) 679endfunction 680 681function! plug#helptags() 682 if !exists('g:plugs') 683 return s:err('plug#begin was not called') 684 endif 685 for spec in values(g:plugs) 686 let docd = join([s:rtp(spec), 'doc'], '/') 687 if isdirectory(docd) 688 silent! execute 'helptags' s:esc(docd) 689 endif 690 endfor 691 return 1 692endfunction 693 694function! s:syntax() 695 syntax clear 696 syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber 697 syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX 698 syn match plugNumber /[0-9]\+[0-9.]*/ contained 699 syn match plugBracket /[[\]]/ contained 700 syn match plugX /x/ contained 701 syn match plugDash /^-/ 702 syn match plugPlus /^+/ 703 syn match plugStar /^*/ 704 syn match plugMessage /\(^- \)\@<=.*/ 705 syn match plugName /\(^- \)\@<=[^ ]*:/ 706 syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ 707 syn match plugTag /(tag: [^)]\+)/ 708 syn match plugInstall /\(^+ \)\@<=[^:]*/ 709 syn match plugUpdate /\(^* \)\@<=[^:]*/ 710 syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag 711 syn match plugEdge /^ \X\+$/ 712 syn match plugEdge /^ \X*/ contained nextgroup=plugSha 713 syn match plugSha /[0-9a-f]\{7,9}/ contained 714 syn match plugRelDate /([^)]*)$/ contained 715 syn match plugNotLoaded /(not loaded)$/ 716 syn match plugError /^x.*/ 717 syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ 718 syn match plugH2 /^.*:\n-\+$/ 719 syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean 720 hi def link plug1 Title 721 hi def link plug2 Repeat 722 hi def link plugH2 Type 723 hi def link plugX Exception 724 hi def link plugBracket Structure 725 hi def link plugNumber Number 726 727 hi def link plugDash Special 728 hi def link plugPlus Constant 729 hi def link plugStar Boolean 730 731 hi def link plugMessage Function 732 hi def link plugName Label 733 hi def link plugInstall Function 734 hi def link plugUpdate Type 735 736 hi def link plugError Error 737 hi def link plugDeleted Ignore 738 hi def link plugRelDate Comment 739 hi def link plugEdge PreProc 740 hi def link plugSha Identifier 741 hi def link plugTag Constant 742 743 hi def link plugNotLoaded Comment 744endfunction 745 746function! s:lpad(str, len) 747 return a:str . repeat(' ', a:len - len(a:str)) 748endfunction 749 750function! s:lines(msg) 751 return split(a:msg, "[\r\n]") 752endfunction 753 754function! s:lastline(msg) 755 return get(s:lines(a:msg), -1, '') 756endfunction 757 758function! s:new_window() 759 execute get(g:, 'plug_window', 'vertical topleft new') 760endfunction 761 762function! s:plug_window_exists() 763 let buflist = tabpagebuflist(s:plug_tab) 764 return !empty(buflist) && index(buflist, s:plug_buf) >= 0 765endfunction 766 767function! s:switch_in() 768 if !s:plug_window_exists() 769 return 0 770 endif 771 772 if winbufnr(0) != s:plug_buf 773 let s:pos = [tabpagenr(), winnr(), winsaveview()] 774 execute 'normal!' s:plug_tab.'gt' 775 let winnr = bufwinnr(s:plug_buf) 776 execute winnr.'wincmd w' 777 call add(s:pos, winsaveview()) 778 else 779 let s:pos = [winsaveview()] 780 endif 781 782 setlocal modifiable 783 return 1 784endfunction 785 786function! s:switch_out(...) 787 call winrestview(s:pos[-1]) 788 setlocal nomodifiable 789 if a:0 > 0 790 execute a:1 791 endif 792 793 if len(s:pos) > 1 794 execute 'normal!' s:pos[0].'gt' 795 execute s:pos[1] 'wincmd w' 796 call winrestview(s:pos[2]) 797 endif 798endfunction 799 800function! s:finish_bindings() 801 nnoremap <silent> <buffer> R :call <SID>retry()<cr> 802 nnoremap <silent> <buffer> D :PlugDiff<cr> 803 nnoremap <silent> <buffer> S :PlugStatus<cr> 804 nnoremap <silent> <buffer> U :call <SID>status_update()<cr> 805 xnoremap <silent> <buffer> U :call <SID>status_update()<cr> 806 nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr> 807 nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr> 808endfunction 809 810function! s:prepare(...) 811 if empty(s:plug_getcwd()) 812 throw 'Invalid current working directory. Cannot proceed.' 813 endif 814 815 for evar in ['$GIT_DIR', '$GIT_WORK_TREE'] 816 if exists(evar) 817 throw evar.' detected. Cannot proceed.' 818 endif 819 endfor 820 821 call s:job_abort() 822 if s:switch_in() 823 if b:plug_preview == 1 824 pc 825 endif 826 enew 827 else 828 call s:new_window() 829 endif 830 831 nnoremap <silent> <buffer> q :if b:plug_preview==1<bar>pc<bar>endif<bar>bd<cr> 832 if a:0 == 0 833 call s:finish_bindings() 834 endif 835 let b:plug_preview = -1 836 let s:plug_tab = tabpagenr() 837 let s:plug_buf = winbufnr(0) 838 call s:assign_name() 839 840 for k in ['<cr>', 'L', 'o', 'X', 'd', 'dd'] 841 execute 'silent! unmap <buffer>' k 842 endfor 843 setlocal buftype=nofile bufhidden=wipe nobuflisted nolist noswapfile nowrap cursorline modifiable nospell 844 if exists('+colorcolumn') 845 setlocal colorcolumn= 846 endif 847 setf vim-plug 848 if exists('g:syntax_on') 849 call s:syntax() 850 endif 851endfunction 852 853function! s:assign_name() 854 " Assign buffer name 855 let prefix = '[Plugins]' 856 let name = prefix 857 let idx = 2 858 while bufexists(name) 859 let name = printf('%s (%s)', prefix, idx) 860 let idx = idx + 1 861 endwhile 862 silent! execute 'f' fnameescape(name) 863endfunction 864 865function! s:chsh(swap) 866 let prev = [&shell, &shellcmdflag, &shellredir] 867 if !s:is_win 868 set shell=sh 869 endif 870 if a:swap 871 if &shell =~# 'powershell\.exe' || &shell =~# 'pwsh$' 872 let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s' 873 elseif &shell =~# 'sh' || &shell =~# 'cmd\.exe' 874 set shellredir=>%s\ 2>&1 875 endif 876 endif 877 return prev 878endfunction 879 880function! s:bang(cmd, ...) 881 let batchfile = '' 882 try 883 let [sh, shellcmdflag, shrd] = s:chsh(a:0) 884 " FIXME: Escaping is incomplete. We could use shellescape with eval, 885 " but it won't work on Windows. 886 let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd 887 if s:is_win 888 let [batchfile, cmd] = s:batchfile(cmd) 889 endif 890 let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%') 891 execute "normal! :execute g:_plug_bang\<cr>\<cr>" 892 finally 893 unlet g:_plug_bang 894 let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] 895 if s:is_win && filereadable(batchfile) 896 call delete(batchfile) 897 endif 898 endtry 899 return v:shell_error ? 'Exit status: ' . v:shell_error : '' 900endfunction 901 902function! s:regress_bar() 903 let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '') 904 call s:progress_bar(2, bar, len(bar)) 905endfunction 906 907function! s:is_updated(dir) 908 return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir)) 909endfunction 910 911function! s:do(pull, force, todo) 912 for [name, spec] in items(a:todo) 913 if !isdirectory(spec.dir) 914 continue 915 endif 916 let installed = has_key(s:update.new, name) 917 let updated = installed ? 0 : 918 \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir)) 919 if a:force || installed || updated 920 execute 'cd' s:esc(spec.dir) 921 call append(3, '- Post-update hook for '. name .' ... ') 922 let error = '' 923 let type = type(spec.do) 924 if type == s:TYPE.string 925 if spec.do[0] == ':' 926 if !get(s:loaded, name, 0) 927 let s:loaded[name] = 1 928 call s:reorg_rtp() 929 endif 930 call s:load_plugin(spec) 931 try 932 execute spec.do[1:] 933 catch 934 let error = v:exception 935 endtry 936 if !s:plug_window_exists() 937 cd - 938 throw 'Warning: vim-plug was terminated by the post-update hook of '.name 939 endif 940 else 941 let error = s:bang(spec.do) 942 endif 943 elseif type == s:TYPE.funcref 944 try 945 call s:load_plugin(spec) 946 let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') 947 call spec.do({ 'name': name, 'status': status, 'force': a:force }) 948 catch 949 let error = v:exception 950 endtry 951 else 952 let error = 'Invalid hook type' 953 endif 954 call s:switch_in() 955 call setline(4, empty(error) ? (getline(4) . 'OK') 956 \ : ('x' . getline(4)[1:] . error)) 957 if !empty(error) 958 call add(s:update.errors, name) 959 call s:regress_bar() 960 endif 961 cd - 962 endif 963 endfor 964endfunction 965 966function! s:hash_match(a, b) 967 return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0 968endfunction 969 970function! s:checkout(spec) 971 let sha = a:spec.commit 972 let output = s:system(['git', 'rev-parse', 'HEAD'], a:spec.dir) 973 if !v:shell_error && !s:hash_match(sha, s:lines(output)[0]) 974 let output = s:system( 975 \ 'git fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir) 976 endif 977 return output 978endfunction 979 980function! s:finish(pull) 981 let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) 982 if new_frozen 983 let s = new_frozen > 1 ? 's' : '' 984 call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s)) 985 endif 986 call append(3, '- Finishing ... ') | 4 987 redraw 988 call plug#helptags() 989 call plug#end() 990 call setline(4, getline(4) . 'Done!') 991 redraw 992 let msgs = [] 993 if !empty(s:update.errors) 994 call add(msgs, "Press 'R' to retry.") 995 endif 996 if a:pull && len(s:update.new) < len(filter(getline(5, '$'), 997 \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'")) 998 call add(msgs, "Press 'D' to see the updated changes.") 999 endif 1000 echo join(msgs, ' ') 1001 call s:finish_bindings() 1002endfunction 1003 1004function! s:retry() 1005 if empty(s:update.errors) 1006 return 1007 endif 1008 echo 1009 call s:update_impl(s:update.pull, s:update.force, 1010 \ extend(copy(s:update.errors), [s:update.threads])) 1011endfunction 1012 1013function! s:is_managed(name) 1014 return has_key(g:plugs[a:name], 'uri') 1015endfunction 1016 1017function! s:names(...) 1018 return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) 1019endfunction 1020 1021function! s:check_ruby() 1022 silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'") 1023 if !exists('g:plug_ruby') 1024 redraw! 1025 return s:warn('echom', 'Warning: Ruby interface is broken') 1026 endif 1027 let ruby_version = split(g:plug_ruby, '\.') 1028 unlet g:plug_ruby 1029 return s:version_requirement(ruby_version, [1, 8, 7]) 1030endfunction 1031 1032function! s:update_impl(pull, force, args) abort 1033 let sync = index(a:args, '--sync') >= 0 || has('vim_starting') 1034 let args = filter(copy(a:args), 'v:val != "--sync"') 1035 let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? 1036 \ remove(args, -1) : get(g:, 'plug_threads', 16) 1037 1038 let managed = filter(copy(g:plugs), 's:is_managed(v:key)') 1039 let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : 1040 \ filter(managed, 'index(args, v:key) >= 0') 1041 1042 if empty(todo) 1043 return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install')) 1044 endif 1045 1046 if !s:is_win && s:git_version_requirement(2, 3) 1047 let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : '' 1048 let $GIT_TERMINAL_PROMPT = 0 1049 for plug in values(todo) 1050 let plug.uri = substitute(plug.uri, 1051 \ '^https://git::@github\.com', 'https://github.com', '') 1052 endfor 1053 endif 1054 1055 if !isdirectory(g:plug_home) 1056 try 1057 call mkdir(g:plug_home, 'p') 1058 catch 1059 return s:err(printf('Invalid plug directory: %s. '. 1060 \ 'Try to call plug#begin with a valid directory', g:plug_home)) 1061 endtry 1062 endif 1063 1064 if has('nvim') && !exists('*jobwait') && threads > 1 1065 call s:warn('echom', '[vim-plug] Update Neovim for parallel installer') 1066 endif 1067 1068 let use_job = s:nvim || s:vim8 1069 let python = (has('python') || has('python3')) && !use_job 1070 let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby() 1071 1072 let s:update = { 1073 \ 'start': reltime(), 1074 \ 'all': todo, 1075 \ 'todo': copy(todo), 1076 \ 'errors': [], 1077 \ 'pull': a:pull, 1078 \ 'force': a:force, 1079 \ 'new': {}, 1080 \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1, 1081 \ 'bar': '', 1082 \ 'fin': 0 1083 \ } 1084 1085 call s:prepare(1) 1086 call append(0, ['', '']) 1087 normal! 2G 1088 silent! redraw 1089 1090 let s:clone_opt = [] 1091 if get(g:, 'plug_shallow', 1) 1092 call extend(s:clone_opt, ['--depth', '1']) 1093 if s:git_version_requirement(1, 7, 10) 1094 call add(s:clone_opt, '--no-single-branch') 1095 endif 1096 endif 1097 1098 if has('win32unix') || has('wsl') 1099 call extend(s:clone_opt, ['-c', 'core.eol=lf', '-c', 'core.autocrlf=input']) 1100 endif 1101 1102 let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : '' 1103 1104 " Python version requirement (>= 2.7) 1105 if python && !has('python3') && !ruby && !use_job && s:update.threads > 1 1106 redir => pyv 1107 silent python import platform; print platform.python_version() 1108 redir END 1109 let python = s:version_requirement( 1110 \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) 1111 endif 1112 1113 if (python || ruby) && s:update.threads > 1 1114 try 1115 let imd = &imd 1116 if s:mac_gui 1117 set noimd 1118 endif 1119 if ruby 1120 call s:update_ruby() 1121 else 1122 call s:update_python() 1123 endif 1124 catch 1125 let lines = getline(4, '$') 1126 let printed = {} 1127 silent! 4,$d _ 1128 for line in lines 1129 let name = s:extract_name(line, '.', '') 1130 if empty(name) || !has_key(printed, name) 1131 call append('$', line) 1132 if !empty(name) 1133 let printed[name] = 1 1134 if line[0] == 'x' && index(s:update.errors, name) < 0 1135 call add(s:update.errors, name) 1136 end 1137 endif 1138 endif 1139 endfor 1140 finally 1141 let &imd = imd 1142 call s:update_finish() 1143 endtry 1144 else 1145 call s:update_vim() 1146 while use_job && sync 1147 sleep 100m 1148 if s:update.fin 1149 break 1150 endif 1151 endwhile 1152 endif 1153endfunction 1154 1155function! s:log4(name, msg) 1156 call setline(4, printf('- %s (%s)', a:msg, a:name)) 1157 redraw 1158endfunction 1159 1160function! s:update_finish() 1161 if exists('s:git_terminal_prompt') 1162 let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt 1163 endif 1164 if s:switch_in() 1165 call append(3, '- Updating ...') | 4 1166 for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))')) 1167 let [pos, _] = s:logpos(name) 1168 if !pos 1169 continue 1170 endif 1171 if has_key(spec, 'commit') 1172 call s:log4(name, 'Checking out '.spec.commit) 1173 let out = s:checkout(spec) 1174 elseif has_key(spec, 'tag') 1175 let tag = spec.tag 1176 if tag =~ '\*' 1177 let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir)) 1178 if !v:shell_error && !empty(tags) 1179 let tag = tags[0] 1180 call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag)) 1181 call append(3, '') 1182 endif 1183 endif 1184 call s:log4(name, 'Checking out '.tag) 1185 let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir) 1186 else 1187 let branch = get(spec, 'branch', 'master') 1188 call s:log4(name, 'Merging origin/'.s:esc(branch)) 1189 let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1' 1190 \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir) 1191 endif 1192 if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && 1193 \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir)) 1194 call s:log4(name, 'Updating submodules. This may take a while.') 1195 let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir) 1196 endif 1197 let msg = s:format_message(v:shell_error ? 'x': '-', name, out) 1198 if v:shell_error 1199 call add(s:update.errors, name) 1200 call s:regress_bar() 1201 silent execute pos 'd _' 1202 call append(4, msg) | 4 1203 elseif !empty(out) 1204 call setline(pos, msg[0]) 1205 endif 1206 redraw 1207 endfor 1208 silent 4 d _ 1209 try 1210 call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")')) 1211 catch 1212 call s:warn('echom', v:exception) 1213 call s:warn('echo', '') 1214 return 1215 endtry 1216 call s:finish(s:update.pull) 1217 call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') 1218 call s:switch_out('normal! gg') 1219 endif 1220endfunction 1221 1222function! s:job_abort() 1223 if (!s:nvim && !s:vim8) || !exists('s:jobs') 1224 return 1225 endif 1226 1227 for [name, j] in items(s:jobs) 1228 if s:nvim 1229 silent! call jobstop(j.jobid) 1230 elseif s:vim8 1231 silent! call job_stop(j.jobid) 1232 endif 1233 if j.new 1234 call s:rm_rf(g:plugs[name].dir) 1235 endif 1236 endfor 1237 let s:jobs = {} 1238endfunction 1239 1240function! s:last_non_empty_line(lines) 1241 let len = len(a:lines) 1242 for idx in range(len) 1243 let line = a:lines[len-idx-1] 1244 if !empty(line) 1245 return line 1246 endif 1247 endfor 1248 return '' 1249endfunction 1250 1251function! s:job_out_cb(self, data) abort 1252 let self = a:self 1253 let data = remove(self.lines, -1) . a:data 1254 let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]') 1255 call extend(self.lines, lines) 1256 " To reduce the number of buffer updates 1257 let self.tick = get(self, 'tick', -1) + 1 1258 if !self.running || self.tick % len(s:jobs) == 0 1259 let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-') 1260 let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines) 1261 call s:log(bullet, self.name, result) 1262 endif 1263endfunction 1264 1265function! s:job_exit_cb(self, data) abort 1266 let a:self.running = 0 1267 let a:self.error = a:data != 0 1268 call s:reap(a:self.name) 1269 call s:tick() 1270endfunction 1271 1272function! s:job_cb(fn, job, ch, data) 1273 if !s:plug_window_exists() " plug window closed 1274 return s:job_abort() 1275 endif 1276 call call(a:fn, [a:job, a:data]) 1277endfunction 1278 1279function! s:nvim_cb(job_id, data, event) dict abort 1280 return (a:event == 'stdout' || a:event == 'stderr') ? 1281 \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) : 1282 \ s:job_cb('s:job_exit_cb', self, 0, a:data) 1283endfunction 1284 1285function! s:spawn(name, cmd, opts) 1286 let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''], 1287 \ 'new': get(a:opts, 'new', 0) } 1288 let s:jobs[a:name] = job 1289 1290 if s:nvim 1291 if has_key(a:opts, 'dir') 1292 let job.cwd = a:opts.dir 1293 endif 1294 let argv = a:cmd 1295 call extend(job, { 1296 \ 'on_stdout': function('s:nvim_cb'), 1297 \ 'on_stderr': function('s:nvim_cb'), 1298 \ 'on_exit': function('s:nvim_cb'), 1299 \ }) 1300 let jid = s:plug_call('jobstart', argv, job) 1301 if jid > 0 1302 let job.jobid = jid 1303 else 1304 let job.running = 0 1305 let job.error = 1 1306 let job.lines = [jid < 0 ? argv[0].' is not executable' : 1307 \ 'Invalid arguments (or job table is full)'] 1308 endif 1309 elseif s:vim8 1310 let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})')) 1311 if has_key(a:opts, 'dir') 1312 let cmd = s:with_cd(cmd, a:opts.dir, 0) 1313 endif 1314 let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd] 1315 let jid = job_start(s:is_win ? join(argv, ' ') : argv, { 1316 \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]), 1317 \ 'err_cb': function('s:job_cb', ['s:job_out_cb', job]), 1318 \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]), 1319 \ 'err_mode': 'raw', 1320 \ 'out_mode': 'raw' 1321 \}) 1322 if job_status(jid) == 'run' 1323 let job.jobid = jid 1324 else 1325 let job.running = 0 1326 let job.error = 1 1327 let job.lines = ['Failed to start job'] 1328 endif 1329 else 1330 let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd])) 1331 let job.error = v:shell_error != 0 1332 let job.running = 0 1333 endif 1334endfunction 1335 1336function! s:reap(name) 1337 let job = s:jobs[a:name] 1338 if job.error 1339 call add(s:update.errors, a:name) 1340 elseif get(job, 'new', 0) 1341 let s:update.new[a:name] = 1 1342 endif 1343 let s:update.bar .= job.error ? 'x' : '=' 1344 1345 let bullet = job.error ? 'x' : '-' 1346 let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines) 1347 call s:log(bullet, a:name, empty(result) ? 'OK' : result) 1348 call s:bar() 1349 1350 call remove(s:jobs, a:name) 1351endfunction 1352 1353function! s:bar() 1354 if s:switch_in() 1355 let total = len(s:update.all) 1356 call setline(1, (s:update.pull ? 'Updating' : 'Installing'). 1357 \ ' plugins ('.len(s:update.bar).'/'.total.')') 1358 call s:progress_bar(2, s:update.bar, total) 1359 call s:switch_out() 1360 endif 1361endfunction 1362 1363function! s:logpos(name) 1364 let max = line('$') 1365 for i in range(4, max > 4 ? max : 4) 1366 if getline(i) =~# '^[-+x*] '.a:name.':' 1367 for j in range(i + 1, max > 5 ? max : 5) 1368 if getline(j) !~ '^ ' 1369 return [i, j - 1] 1370 endif 1371 endfor 1372 return [i, i] 1373 endif 1374 endfor 1375 return [0, 0] 1376endfunction 1377 1378function! s:log(bullet, name, lines) 1379 if s:switch_in() 1380 let [b, e] = s:logpos(a:name) 1381 if b > 0 1382 silent execute printf('%d,%d d _', b, e) 1383 if b > winheight('.') 1384 let b = 4 1385 endif 1386 else 1387 let b = 4 1388 endif 1389 " FIXME For some reason, nomodifiable is set after :d in vim8 1390 setlocal modifiable 1391 call append(b - 1, s:format_message(a:bullet, a:name, a:lines)) 1392 call s:switch_out() 1393 endif 1394endfunction 1395 1396function! s:update_vim() 1397 let s:jobs = {} 1398 1399 call s:bar() 1400 call s:tick() 1401endfunction 1402 1403function! s:tick() 1404 let pull = s:update.pull 1405 let prog = s:progress_opt(s:nvim || s:vim8) 1406while 1 " Without TCO, Vim stack is bound to explode 1407 if empty(s:update.todo) 1408 if empty(s:jobs) && !s:update.fin 1409 call s:update_finish() 1410 let s:update.fin = 1 1411 endif 1412 return 1413 endif 1414 1415 let name = keys(s:update.todo)[0] 1416 let spec = remove(s:update.todo, name) 1417 let new = empty(globpath(spec.dir, '.git', 1)) 1418 1419 call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') 1420 redraw 1421 1422 let has_tag = has_key(spec, 'tag') 1423 if !new 1424 let [error, _] = s:git_validate(spec, 0) 1425 if empty(error) 1426 if pull 1427 let cmd = ['git', 'fetch'] 1428 if has_tag && !empty(globpath(spec.dir, '.git/shallow')) 1429 call extend(cmd, ['--depth', '99999999']) 1430 endif 1431 if !empty(prog) 1432 call add(cmd, prog) 1433 endif 1434 call s:spawn(name, cmd, { 'dir': spec.dir }) 1435 else 1436 let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } 1437 endif 1438 else 1439 let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } 1440 endif 1441 else 1442 let cmd = ['git', 'clone'] 1443 if !has_tag 1444 call extend(cmd, s:clone_opt) 1445 endif 1446 if !empty(prog) 1447 call add(cmd, prog) 1448 endif 1449 call s:spawn(name, extend(cmd, [spec.uri, s:trim(spec.dir)]), { 'new': 1 }) 1450 endif 1451 1452 if !s:jobs[name].running 1453 call s:reap(name) 1454 endif 1455 if len(s:jobs) >= s:update.threads 1456 break 1457 endif 1458endwhile 1459endfunction 1460 1461function! s:update_python() 1462let py_exe = has('python') ? 'python' : 'python3' 1463execute py_exe "<< EOF" 1464import datetime 1465import functools 1466import os 1467try: 1468 import queue 1469except ImportError: 1470 import Queue as queue 1471import random 1472import re 1473import shutil 1474import signal 1475import subprocess 1476import tempfile 1477import threading as thr 1478import time 1479import traceback 1480import vim 1481 1482G_NVIM = vim.eval("has('nvim')") == '1' 1483G_PULL = vim.eval('s:update.pull') == '1' 1484G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 1485G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) 1486G_CLONE_OPT = ' '.join(vim.eval('s:clone_opt')) 1487G_PROGRESS = vim.eval('s:progress_opt(1)') 1488G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) 1489G_STOP = thr.Event() 1490G_IS_WIN = vim.eval('s:is_win') == '1' 1491 1492class PlugError(Exception): 1493 def __init__(self, msg): 1494 self.msg = msg 1495class CmdTimedOut(PlugError): 1496 pass 1497class CmdFailed(PlugError): 1498 pass 1499class InvalidURI(PlugError): 1500 pass 1501class Action(object): 1502 INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] 1503 1504class Buffer(object): 1505 def __init__(self, lock, num_plugs, is_pull): 1506 self.bar = '' 1507 self.event = 'Updating' if is_pull else 'Installing' 1508 self.lock = lock 1509 self.maxy = int(vim.eval('winheight(".")')) 1510 self.num_plugs = num_plugs 1511 1512 def __where(self, name): 1513 """ Find first line with name in current buffer. Return line num. """ 1514 found, lnum = False, 0 1515 matcher = re.compile('^[-+x*] {0}:'.format(name)) 1516 for line in vim.current.buffer: 1517 if matcher.search(line) is not None: 1518 found = True 1519 break 1520 lnum += 1 1521 1522 if not found: 1523 lnum = -1 1524 return lnum 1525 1526 def header(self): 1527 curbuf = vim.current.buffer 1528 curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs) 1529 1530 num_spaces = self.num_plugs - len(self.bar) 1531 curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') 1532 1533 with self.lock: 1534 vim.command('normal! 2G') 1535 vim.command('redraw') 1536 1537 def write(self, action, name, lines): 1538 first, rest = lines[0], lines[1:] 1539 msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] 1540 msg.extend([' ' + line for line in rest]) 1541 1542 try: 1543 if action == Action.ERROR: 1544 self.bar += 'x' 1545 vim.command("call add(s:update.errors, '{0}')".format(name)) 1546 elif action == Action.DONE: 1547 self.bar += '=' 1548 1549 curbuf = vim.current.buffer 1550 lnum = self.__where(name) 1551 if lnum != -1: # Found matching line num 1552 del curbuf[lnum] 1553 if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): 1554 lnum = 3 1555 else: 1556 lnum = 3 1557 curbuf.append(msg, lnum) 1558 1559 self.header() 1560 except vim.error: 1561 pass 1562 1563class Command(object): 1564 CD = 'cd /d' if G_IS_WIN else 'cd' 1565 1566 def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): 1567 self.cmd = cmd 1568 if cmd_dir: 1569 self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd) 1570 self.timeout = timeout 1571 self.callback = cb if cb else (lambda msg: None) 1572 self.clean = clean if clean else (lambda: None) 1573 self.proc = None 1574 1575 @property 1576 def alive(self): 1577 """ Returns true only if command still running. """ 1578 return self.proc and self.proc.poll() is None 1579 1580 def execute(self, ntries=3): 1581 """ Execute the command with ntries if CmdTimedOut. 1582 Returns the output of the command if no Exception. 1583 """ 1584 attempt, finished, limit = 0, False, self.timeout 1585 1586 while not finished: 1587 try: 1588 attempt += 1 1589 result = self.try_command() 1590 finished = True 1591 return result 1592 except CmdTimedOut: 1593 if attempt != ntries: 1594 self.notify_retry() 1595 self.timeout += limit 1596 else: 1597 raise 1598 1599 def notify_retry(self): 1600 """ Retry required for command, notify user. """ 1601 for count in range(3, 0, -1): 1602 if G_STOP.is_set(): 1603 raise KeyboardInterrupt 1604 msg = 'Timeout. Will retry in {0} second{1} ...'.format( 1605 count, 's' if count != 1 else '') 1606 self.callback([msg]) 1607 time.sleep(1) 1608 self.callback(['Retrying ...']) 1609 1610 def try_command(self): 1611 """ Execute a cmd & poll for callback. Returns list of output. 1612 Raises CmdFailed -> return code for Popen isn't 0 1613 Raises CmdTimedOut -> command exceeded timeout without new output 1614 """ 1615 first_line = True 1616 1617 try: 1618 tfile = tempfile.NamedTemporaryFile(mode='w+b') 1619 preexec_fn = not G_IS_WIN and os.setsid or None 1620 self.proc = subprocess.Popen(self.cmd, stdout=tfile, 1621 stderr=subprocess.STDOUT, 1622 stdin=subprocess.PIPE, shell=True, 1623 preexec_fn=preexec_fn) 1624 thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) 1625 thrd.start() 1626 1627 thread_not_started = True 1628 while thread_not_started: 1629 try: 1630 thrd.join(0.1) 1631 thread_not_started = False 1632 except RuntimeError: 1633 pass 1634 1635 while self.alive: 1636 if G_STOP.is_set(): 1637 raise KeyboardInterrupt 1638 1639 if first_line or random.random() < G_LOG_PROB: 1640 first_line = False 1641 line = '' if G_IS_WIN else nonblock_read(tfile.name) 1642 if line: 1643 self.callback([line]) 1644 1645 time_diff = time.time() - os.path.getmtime(tfile.name) 1646 if time_diff > self.timeout: 1647 raise CmdTimedOut(['Timeout!']) 1648 1649 thrd.join(0.5) 1650 1651 tfile.seek(0) 1652 result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] 1653 1654 if self.proc.returncode != 0: 1655 raise CmdFailed([''] + result) 1656 1657 return result 1658 except: 1659 self.terminate() 1660 raise 1661 1662 def terminate(self): 1663 """ Terminate process and cleanup. """ 1664 if self.alive: 1665 if G_IS_WIN: 1666 os.kill(self.proc.pid, signal.SIGINT) 1667 else: 1668 os.killpg(self.proc.pid, signal.SIGTERM) 1669 self.clean() 1670 1671class Plugin(object): 1672 def __init__(self, name, args, buf_q, lock): 1673 self.name = name 1674 self.args = args 1675 self.buf_q = buf_q 1676 self.lock = lock 1677 self.tag = args.get('tag', 0) 1678 1679 def manage(self): 1680 try: 1681 if os.path.exists(self.args['dir']): 1682 self.update() 1683 else: 1684 self.install() 1685 with self.lock: 1686 thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) 1687 except PlugError as exc: 1688 self.write(Action.ERROR, self.name, exc.msg) 1689 except KeyboardInterrupt: 1690 G_STOP.set() 1691 self.write(Action.ERROR, self.name, ['Interrupted!']) 1692 except: 1693 # Any exception except those above print stack trace 1694 msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip()) 1695 self.write(Action.ERROR, self.name, msg.split('\n')) 1696 raise 1697 1698 def install(self): 1699 target = self.args['dir'] 1700 if target[-1] == '\\': 1701 target = target[0:-1] 1702 1703 def clean(target): 1704 def _clean(): 1705 try: 1706 shutil.rmtree(target) 1707 except OSError: 1708 pass 1709 return _clean 1710 1711 self.write(Action.INSTALL, self.name, ['Installing ...']) 1712 callback = functools.partial(self.write, Action.INSTALL, self.name) 1713 cmd = 'git clone {0} {1} {2} {3} 2>&1'.format( 1714 '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], 1715 esc(target)) 1716 com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) 1717 result = com.execute(G_RETRIES) 1718 self.write(Action.DONE, self.name, result[-1:]) 1719 1720 def repo_uri(self): 1721 cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url' 1722 command = Command(cmd, self.args['dir'], G_TIMEOUT,) 1723 result = command.execute(G_RETRIES) 1724 return result[-1] 1725 1726 def update(self): 1727 actual_uri = self.repo_uri() 1728 expect_uri = self.args['uri'] 1729 regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$') 1730 ma = regex.match(actual_uri) 1731 mb = regex.match(expect_uri) 1732 if ma is None or mb is None or ma.groups() != mb.groups(): 1733 msg = ['', 1734 'Invalid URI: {0}'.format(actual_uri), 1735 'Expected {0}'.format(expect_uri), 1736 'PlugClean required.'] 1737 raise InvalidURI(msg) 1738 1739 if G_PULL: 1740 self.write(Action.UPDATE, self.name, ['Updating ...']) 1741 callback = functools.partial(self.write, Action.UPDATE, self.name) 1742 fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' 1743 cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS) 1744 com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) 1745 result = com.execute(G_RETRIES) 1746 self.write(Action.DONE, self.name, result[-1:]) 1747 else: 1748 self.write(Action.DONE, self.name, ['Already installed']) 1749 1750 def write(self, action, name, msg): 1751 self.buf_q.put((action, name, msg)) 1752 1753class PlugThread(thr.Thread): 1754 def __init__(self, tname, args): 1755 super(PlugThread, self).__init__() 1756 self.tname = tname 1757 self.args = args 1758 1759 def run(self): 1760 thr.current_thread().name = self.tname 1761 buf_q, work_q, lock = self.args 1762 1763 try: 1764 while not G_STOP.is_set(): 1765 name, args = work_q.get_nowait() 1766 plug = Plugin(name, args, buf_q, lock) 1767 plug.manage() 1768 work_q.task_done() 1769 except queue.Empty: 1770 pass 1771 1772class RefreshThread(thr.Thread): 1773 def __init__(self, lock): 1774 super(RefreshThread, self).__init__() 1775 self.lock = lock 1776 self.running = True 1777 1778 def run(self): 1779 while self.running: 1780 with self.lock: 1781 thread_vim_command('noautocmd normal! a') 1782 time.sleep(0.33) 1783 1784 def stop(self): 1785 self.running = False 1786 1787if G_NVIM: 1788 def thread_vim_command(cmd): 1789 vim.session.threadsafe_call(lambda: vim.command(cmd)) 1790else: 1791 def thread_vim_command(cmd): 1792 vim.command(cmd) 1793 1794def esc(name): 1795 return '"' + name.replace('"', '\"') + '"' 1796 1797def nonblock_read(fname): 1798 """ Read a file with nonblock flag. Return the last line. """ 1799 fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK) 1800 buf = os.read(fread, 100000).decode('utf-8', 'replace') 1801 os.close(fread) 1802 1803 line = buf.rstrip('\r\n') 1804 left = max(line.rfind('\r'), line.rfind('\n')) 1805 if left != -1: 1806 left += 1 1807 line = line[left:] 1808 1809 return line 1810 1811def main(): 1812 thr.current_thread().name = 'main' 1813 nthreads = int(vim.eval('s:update.threads')) 1814 plugs = vim.eval('s:update.todo') 1815 mac_gui = vim.eval('s:mac_gui') == '1' 1816 1817 lock = thr.Lock() 1818 buf = Buffer(lock, len(plugs), G_PULL) 1819 buf_q, work_q = queue.Queue(), queue.Queue() 1820 for work in plugs.items(): 1821 work_q.put(work) 1822 1823 start_cnt = thr.active_count() 1824 for num in range(nthreads): 1825 tname = 'PlugT-{0:02}'.format(num) 1826 thread = PlugThread(tname, (buf_q, work_q, lock)) 1827 thread.start() 1828 if mac_gui: 1829 rthread = RefreshThread(lock) 1830 rthread.start() 1831 1832 while not buf_q.empty() or thr.active_count() != start_cnt: 1833 try: 1834 action, name, msg = buf_q.get(True, 0.25) 1835 buf.write(action, name, ['OK'] if not msg else msg) 1836 buf_q.task_done() 1837 except queue.Empty: 1838 pass 1839 except KeyboardInterrupt: 1840 G_STOP.set() 1841 1842 if mac_gui: 1843 rthread.stop() 1844 rthread.join() 1845 1846main() 1847EOF 1848endfunction 1849 1850function! s:update_ruby() 1851 ruby << EOF 1852 module PlugStream 1853 SEP = ["\r", "\n", nil] 1854 def get_line 1855 buffer = '' 1856 loop do 1857 char = readchar rescue return 1858 if SEP.include? char.chr 1859 buffer << $/ 1860 break 1861 else 1862 buffer << char 1863 end 1864 end 1865 buffer 1866 end 1867 end unless defined?(PlugStream) 1868 1869 def esc arg 1870 %["#{arg.gsub('"', '\"')}"] 1871 end 1872 1873 def killall pid 1874 pids = [pid] 1875 if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM 1876 pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil } 1877 else 1878 unless `which pgrep 2> /dev/null`.empty? 1879 children = pids 1880 until children.empty? 1881 children = children.map { |pid| 1882 `pgrep -P #{pid}`.lines.map { |l| l.chomp } 1883 }.flatten 1884 pids += children 1885 end 1886 end 1887 pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } 1888 end 1889 end 1890 1891 def compare_git_uri a, b 1892 regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$} 1893 regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1) 1894 end 1895 1896 require 'thread' 1897 require 'fileutils' 1898 require 'timeout' 1899 running = true 1900 iswin = VIM::evaluate('s:is_win').to_i == 1 1901 pull = VIM::evaluate('s:update.pull').to_i == 1 1902 base = VIM::evaluate('g:plug_home') 1903 all = VIM::evaluate('s:update.todo') 1904 limit = VIM::evaluate('get(g:, "plug_timeout", 60)') 1905 tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 1906 nthr = VIM::evaluate('s:update.threads').to_i 1907 maxy = VIM::evaluate('winheight(".")').to_i 1908 vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/ 1909 cd = iswin ? 'cd /d' : 'cd' 1910 tot = VIM::evaluate('len(s:update.todo)') || 0 1911 bar = '' 1912 skip = 'Already installed' 1913 mtx = Mutex.new 1914 take1 = proc { mtx.synchronize { running && all.shift } } 1915 logh = proc { 1916 cnt = bar.length 1917 $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" 1918 $curbuf[2] = '[' + bar.ljust(tot) + ']' 1919 VIM::command('normal! 2G') 1920 VIM::command('redraw') 1921 } 1922 where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } 1923 log = proc { |name, result, type| 1924 mtx.synchronize do 1925 ing = ![true, false].include?(type) 1926 bar += type ? '=' : 'x' unless ing 1927 b = case type 1928 when :install then '+' when :update then '*' 1929 when true, nil then '-' else 1930 VIM::command("call add(s:update.errors, '#{name}')") 1931 'x' 1932 end 1933 result = 1934 if type || type.nil? 1935 ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"] 1936 elsif result =~ /^Interrupted|^Timeout/ 1937 ["#{b} #{name}: #{result}"] 1938 else 1939 ["#{b} #{name}"] + result.lines.map { |l| " " << l } 1940 end 1941 if lnum = where.call(name) 1942 $curbuf.delete lnum 1943 lnum = 4 if ing && lnum > maxy 1944 end 1945 result.each_with_index do |line, offset| 1946 $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp) 1947 end 1948 logh.call 1949 end 1950 } 1951 bt = proc { |cmd, name, type, cleanup| 1952 tried = timeout = 0 1953 begin 1954 tried += 1 1955 timeout += limit 1956 fd = nil 1957 data = '' 1958 if iswin 1959 Timeout::timeout(timeout) do 1960 tmp = VIM::evaluate('tempname()') 1961 system("(#{cmd}) > #{tmp}") 1962 data = File.read(tmp).chomp 1963 File.unlink tmp rescue nil 1964 end 1965 else 1966 fd = IO.popen(cmd).extend(PlugStream) 1967 first_line = true 1968 log_prob = 1.0 / nthr 1969 while line = Timeout::timeout(timeout) { fd.get_line } 1970 data << line 1971 log.call name, line.chomp, type if name && (first_line || rand < log_prob) 1972 first_line = false 1973 end 1974 fd.close 1975 end 1976 [$? == 0, data.chomp] 1977 rescue Timeout::Error, Interrupt => e 1978 if fd && !fd.closed? 1979 killall fd.pid 1980 fd.close 1981 end 1982 cleanup.call if cleanup 1983 if e.is_a?(Timeout::Error) && tried < tries 1984 3.downto(1) do |countdown| 1985 s = countdown > 1 ? 's' : '' 1986 log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type 1987 sleep 1 1988 end 1989 log.call name, 'Retrying ...', type 1990 retry 1991 end 1992 [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"] 1993 end 1994 } 1995 main = Thread.current 1996 threads = [] 1997 watcher = Thread.new { 1998 if vim7 1999 while VIM::evaluate('getchar(1)') 2000 sleep 0.1 2001 end 2002 else 2003 require 'io/console' # >= Ruby 1.9 2004 nil until IO.console.getch == 3.chr 2005 end 2006 mtx.synchronize do 2007 running = false 2008 threads.each { |t| t.raise Interrupt } unless vim7 2009 end 2010 threads.each { |t| t.join rescue nil } 2011 main.kill 2012 } 2013 refresh = Thread.new { 2014 while true 2015 mtx.synchronize do 2016 break unless running 2017 VIM::command('noautocmd normal! a') 2018 end 2019 sleep 0.2 2020 end 2021 } if VIM::evaluate('s:mac_gui') == 1 2022 2023 clone_opt = VIM::evaluate('s:clone_opt').join(' ') 2024 progress = VIM::evaluate('s:progress_opt(1)') 2025 nthr.times do 2026 mtx.synchronize do 2027 threads << Thread.new { 2028 while pair = take1.call 2029 name = pair.first 2030 dir, uri, tag = pair.last.values_at *%w[dir uri tag] 2031 exists = File.directory? dir 2032 ok, result = 2033 if exists 2034 chdir = "#{cd} #{iswin ? dir : esc(dir)}" 2035 ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil 2036 current_uri = data.lines.to_a.last 2037 if !ret 2038 if data =~ /^Interrupted|^Timeout/ 2039 [false, data] 2040 else 2041 [false, [data.chomp, "PlugClean required."].join($/)] 2042 end 2043 elsif !compare_git_uri(current_uri, uri) 2044 [false, ["Invalid URI: #{current_uri}", 2045 "Expected: #{uri}", 2046 "PlugClean required."].join($/)] 2047 else 2048 if pull 2049 log.call name, 'Updating ...', :update 2050 fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' 2051 bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil 2052 else 2053 [true, skip] 2054 end 2055 end 2056 else 2057 d = esc dir.sub(%r{[\\/]+$}, '') 2058 log.call name, 'Installing ...', :install 2059 bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc { 2060 FileUtils.rm_rf dir 2061 } 2062 end 2063 mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok 2064 log.call name, result, ok 2065 end 2066 } if running 2067 end 2068 end 2069 threads.each { |t| t.join rescue nil } 2070 logh.call 2071 refresh.kill if refresh 2072 watcher.kill 2073EOF 2074endfunction 2075 2076function! s:shellesc_cmd(arg, script) 2077 let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g') 2078 return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g') 2079endfunction 2080 2081function! s:shellesc_ps1(arg) 2082 return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'" 2083endfunction 2084 2085function! s:shellesc_sh(arg) 2086 return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'" 2087endfunction 2088 2089" Escape the shell argument based on the shell. 2090" Vim and Neovim's shellescape() are insufficient. 2091" 1. shellslash determines whether to use single/double quotes. 2092" Double-quote escaping is fragile for cmd.exe. 2093" 2. It does not work for powershell. 2094" 3. It does not work for *sh shells if the command is executed 2095" via cmd.exe (ie. cmd.exe /c sh -c command command_args) 2096" 4. It does not support batchfile syntax. 2097" 2098" Accepts an optional dictionary with the following keys: 2099" - shell: same as Vim/Neovim 'shell' option. 2100" If unset, fallback to 'cmd.exe' on Windows or 'sh'. 2101" - script: If truthy and shell is cmd.exe, escape for batchfile syntax. 2102function! plug#shellescape(arg, ...) 2103 if a:arg =~# '^[A-Za-z0-9_/:.-]\+$' 2104 return a:arg 2105 endif 2106 let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {} 2107 let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh') 2108 let script = get(opts, 'script', 1) 2109 if shell =~# 'cmd\.exe' 2110 return s:shellesc_cmd(a:arg, script) 2111 elseif shell =~# 'powershell\.exe' || shell =~# 'pwsh$' 2112 return s:shellesc_ps1(a:arg) 2113 endif 2114 return s:shellesc_sh(a:arg) 2115endfunction 2116 2117function! s:glob_dir(path) 2118 return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)') 2119endfunction 2120 2121function! s:progress_bar(line, bar, total) 2122 call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']') 2123endfunction 2124 2125function! s:compare_git_uri(a, b) 2126 " See `git help clone' 2127 " https:// [user@] github.com[:port] / junegunn/vim-plug [.git] 2128 " [git@] github.com[:port] : junegunn/vim-plug [.git] 2129 " file:// / junegunn/vim-plug [/] 2130 " / junegunn/vim-plug [/] 2131 let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$' 2132 let ma = matchlist(a:a, pat) 2133 let mb = matchlist(a:b, pat) 2134 return ma[1:2] ==# mb[1:2] 2135endfunction 2136 2137function! s:format_message(bullet, name, message) 2138 if a:bullet != 'x' 2139 return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] 2140 else 2141 let lines = map(s:lines(a:message), '" ".v:val') 2142 return extend([printf('x %s:', a:name)], lines) 2143 endif 2144endfunction 2145 2146function! s:with_cd(cmd, dir, ...) 2147 let script = a:0 > 0 ? a:1 : 1 2148 return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd) 2149endfunction 2150 2151function! s:system(cmd, ...) 2152 let batchfile = '' 2153 try 2154 let [sh, shellcmdflag, shrd] = s:chsh(1) 2155 if type(a:cmd) == s:TYPE.list 2156 " Neovim's system() supports list argument to bypass the shell 2157 " but it cannot set the working directory for the command. 2158 " Assume that the command does not rely on the shell. 2159 if has('nvim') && a:0 == 0 2160 return system(a:cmd) 2161 endif 2162 let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})')) 2163 if &shell =~# 'powershell\.exe' 2164 let cmd = '& ' . cmd 2165 endif 2166 else 2167 let cmd = a:cmd 2168 endif 2169 if a:0 > 0 2170 let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list) 2171 endif 2172 if s:is_win && type(a:cmd) != s:TYPE.list 2173 let [batchfile, cmd] = s:batchfile(cmd) 2174 endif 2175 return system(cmd) 2176 finally 2177 let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] 2178 if s:is_win && filereadable(batchfile) 2179 call delete(batchfile) 2180 endif 2181 endtry 2182endfunction 2183 2184function! s:system_chomp(...) 2185 let ret = call('s:system', a:000) 2186 return v:shell_error ? '' : substitute(ret, '\n$', '', '') 2187endfunction 2188 2189function! s:git_validate(spec, check_branch) 2190 let err = '' 2191 if isdirectory(a:spec.dir) 2192 let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url', a:spec.dir)) 2193 let remote = result[-1] 2194 if v:shell_error 2195 let err = join([remote, 'PlugClean required.'], "\n") 2196 elseif !s:compare_git_uri(remote, a:spec.uri) 2197 let err = join(['Invalid URI: '.remote, 2198 \ 'Expected: '.a:spec.uri, 2199 \ 'PlugClean required.'], "\n") 2200 elseif a:check_branch && has_key(a:spec, 'commit') 2201 let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir)) 2202 let sha = result[-1] 2203 if v:shell_error 2204 let err = join(add(result, 'PlugClean required.'), "\n") 2205 elseif !s:hash_match(sha, a:spec.commit) 2206 let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', 2207 \ a:spec.commit[:6], sha[:6]), 2208 \ 'PlugUpdate required.'], "\n") 2209 endif 2210 elseif a:check_branch 2211 let branch = result[0] 2212 " Check tag 2213 if has_key(a:spec, 'tag') 2214 let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) 2215 if a:spec.tag !=# tag && a:spec.tag !~ '\*' 2216 let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', 2217 \ (empty(tag) ? 'N/A' : tag), a:spec.tag) 2218 endif 2219 " Check branch 2220 elseif a:spec.branch !=# branch 2221 let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', 2222 \ branch, a:spec.branch) 2223 endif 2224 if empty(err) 2225 let [ahead, behind] = split(s:lastline(s:system([ 2226 \ 'git', 'rev-list', '--count', '--left-right', 2227 \ printf('HEAD...origin/%s', a:spec.branch) 2228 \ ], a:spec.dir)), '\t') 2229 if !v:shell_error && ahead 2230 if behind 2231 " Only mention PlugClean if diverged, otherwise it's likely to be 2232 " pushable (and probably not that messed up). 2233 let err = printf( 2234 \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" 2235 \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind) 2236 else 2237 let err = printf("Ahead of origin/%s by %d commit(s).\n" 2238 \ .'Cannot update until local changes are pushed.', 2239 \ a:spec.branch, ahead) 2240 endif 2241 endif 2242 endif 2243 endif 2244 else 2245 let err = 'Not found' 2246 endif 2247 return [err, err =~# 'PlugClean'] 2248endfunction 2249 2250function! s:rm_rf(dir) 2251 if isdirectory(a:dir) 2252 call s:system(s:is_win 2253 \ ? 'rmdir /S /Q '.plug#shellescape(a:dir) 2254 \ : ['rm', '-rf', a:dir]) 2255 endif 2256endfunction 2257 2258function! s:clean(force) 2259 call s:prepare() 2260 call append(0, 'Searching for invalid plugins in '.g:plug_home) 2261 call append(1, '') 2262 2263 " List of valid directories 2264 let dirs = [] 2265 let errs = {} 2266 let [cnt, total] = [0, len(g:plugs)] 2267 for [name, spec] in items(g:plugs) 2268 if !s:is_managed(name) 2269 call add(dirs, spec.dir) 2270 else 2271 let [err, clean] = s:git_validate(spec, 1) 2272 if clean 2273 let errs[spec.dir] = s:lines(err)[0] 2274 else 2275 call add(dirs, spec.dir) 2276 endif 2277 endif 2278 let cnt += 1 2279 call s:progress_bar(2, repeat('=', cnt), total) 2280 normal! 2G 2281 redraw 2282 endfor 2283 2284 let allowed = {} 2285 for dir in dirs 2286 let allowed[s:dirpath(s:plug_fnamemodify(dir, ':h:h'))] = 1 2287 let allowed[dir] = 1 2288 for child in s:glob_dir(dir) 2289 let allowed[child] = 1 2290 endfor 2291 endfor 2292 2293 let todo = [] 2294 let found = sort(s:glob_dir(g:plug_home)) 2295 while !empty(found) 2296 let f = remove(found, 0) 2297 if !has_key(allowed, f) && isdirectory(f) 2298 call add(todo, f) 2299 call append(line('$'), '- ' . f) 2300 if has_key(errs, f) 2301 call append(line('$'), ' ' . errs[f]) 2302 endif 2303 let found = filter(found, 'stridx(v:val, f) != 0') 2304 end 2305 endwhile 2306 2307 4 2308 redraw 2309 if empty(todo) 2310 call append(line('$'), 'Already clean.') 2311 else 2312 let s:clean_count = 0 2313 call append(3, ['Directories to delete:', '']) 2314 redraw! 2315 if a:force || s:ask_no_interrupt('Delete all directories?') 2316 call s:delete([6, line('$')], 1) 2317 else 2318 call setline(4, 'Cancelled.') 2319 nnoremap <silent> <buffer> d :set opfunc=<sid>delete_op<cr>g@ 2320 nmap <silent> <buffer> dd d_ 2321 xnoremap <silent> <buffer> d :<c-u>call <sid>delete_op(visualmode(), 1)<cr> 2322 echo 'Delete the lines (d{motion}) to delete the corresponding directories' 2323 endif 2324 endif 2325 4 2326 setlocal nomodifiable 2327endfunction 2328 2329function! s:delete_op(type, ...) 2330 call s:delete(a:0 ? [line("'<"), line("'>")] : [line("'["), line("']")], 0) 2331endfunction 2332 2333function! s:delete(range, force) 2334 let [l1, l2] = a:range 2335 let force = a:force 2336 while l1 <= l2 2337 let line = getline(l1) 2338 if line =~ '^- ' && isdirectory(line[2:]) 2339 execute l1 2340 redraw! 2341 let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) 2342 let force = force || answer > 1 2343 if answer 2344 call s:rm_rf(line[2:]) 2345 setlocal modifiable 2346 call setline(l1, '~'.line[1:]) 2347 let s:clean_count += 1 2348 call setline(4, printf('Removed %d directories.', s:clean_count)) 2349 setlocal nomodifiable 2350 endif 2351 endif 2352 let l1 += 1 2353 endwhile 2354endfunction 2355 2356function! s:upgrade() 2357 echo 'Downloading the latest version of vim-plug' 2358 redraw 2359 let tmp = s:plug_tempname() 2360 let new = tmp . '/plug.vim' 2361 2362 try 2363 let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp]) 2364 if v:shell_error 2365 return s:err('Error upgrading vim-plug: '. out) 2366 endif 2367 2368 if readfile(s:me) ==# readfile(new) 2369 echo 'vim-plug is already up-to-date' 2370 return 0 2371 else 2372 call rename(s:me, s:me . '.old') 2373 call rename(new, s:me) 2374 unlet g:loaded_plug 2375 echo 'vim-plug has been upgraded' 2376 return 1 2377 endif 2378 finally 2379 silent! call s:rm_rf(tmp) 2380 endtry 2381endfunction 2382 2383function! s:upgrade_specs() 2384 for spec in values(g:plugs) 2385 let spec.frozen = get(spec, 'frozen', 0) 2386 endfor 2387endfunction 2388 2389function! s:status() 2390 call s:prepare() 2391 call append(0, 'Checking plugins') 2392 call append(1, '') 2393 2394 let ecnt = 0 2395 let unloaded = 0 2396 let [cnt, total] = [0, len(g:plugs)] 2397 for [name, spec] in items(g:plugs) 2398 let is_dir = isdirectory(spec.dir) 2399 if has_key(spec, 'uri') 2400 if is_dir 2401 let [err, _] = s:git_validate(spec, 1) 2402 let [valid, msg] = [empty(err), empty(err) ? 'OK' : err] 2403 else 2404 let [valid, msg] = [0, 'Not found. Try PlugInstall.'] 2405 endif 2406 else 2407 if is_dir 2408 let [valid, msg] = [1, 'OK'] 2409 else 2410 let [valid, msg] = [0, 'Not found.'] 2411 endif 2412 endif 2413 let cnt += 1 2414 let ecnt += !valid 2415 " `s:loaded` entry can be missing if PlugUpgraded 2416 if is_dir && get(s:loaded, name, -1) == 0 2417 let unloaded = 1 2418 let msg .= ' (not loaded)' 2419 endif 2420 call s:progress_bar(2, repeat('=', cnt), total) 2421 call append(3, s:format_message(valid ? '-' : 'x', name, msg)) 2422 normal! 2G 2423 redraw 2424 endfor 2425 call setline(1, 'Finished. '.ecnt.' error(s).') 2426 normal! gg 2427 setlocal nomodifiable 2428 if unloaded 2429 echo "Press 'L' on each line to load plugin, or 'U' to update" 2430 nnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr> 2431 xnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr> 2432 end 2433endfunction 2434 2435function! s:extract_name(str, prefix, suffix) 2436 return matchstr(a:str, '^'.a:prefix.' \zs[^:]\+\ze:.*'.a:suffix.'$') 2437endfunction 2438 2439function! s:status_load(lnum) 2440 let line = getline(a:lnum) 2441 let name = s:extract_name(line, '-', '(not loaded)') 2442 if !empty(name) 2443 call plug#load(name) 2444 setlocal modifiable 2445 call setline(a:lnum, substitute(line, ' (not loaded)$', '', '')) 2446 setlocal nomodifiable 2447 endif 2448endfunction 2449 2450function! s:status_update() range 2451 let lines = getline(a:firstline, a:lastline) 2452 let names = filter(map(lines, 's:extract_name(v:val, "[x-]", "")'), '!empty(v:val)') 2453 if !empty(names) 2454 echo 2455 execute 'PlugUpdate' join(names) 2456 endif 2457endfunction 2458 2459function! s:is_preview_window_open() 2460 silent! wincmd P 2461 if &previewwindow 2462 wincmd p 2463 return 1 2464 endif 2465endfunction 2466 2467function! s:find_name(lnum) 2468 for lnum in reverse(range(1, a:lnum)) 2469 let line = getline(lnum) 2470 if empty(line) 2471 return '' 2472 endif 2473 let name = s:extract_name(line, '-', '') 2474 if !empty(name) 2475 return name 2476 endif 2477 endfor 2478 return '' 2479endfunction 2480 2481function! s:preview_commit() 2482 if b:plug_preview < 0 2483 let b:plug_preview = !s:is_preview_window_open() 2484 endif 2485 2486 let sha = matchstr(getline('.'), '^ \X*\zs[0-9a-f]\{7,9}') 2487 if empty(sha) 2488 return 2489 endif 2490 2491 let name = s:find_name(line('.')) 2492 if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir) 2493 return 2494 endif 2495 2496 if exists('g:plug_pwindow') && !s:is_preview_window_open() 2497 execute g:plug_pwindow 2498 execute 'e' sha 2499 else 2500 execute 'pedit' sha 2501 wincmd P 2502 endif 2503 setlocal previewwindow filetype=git buftype=nofile nobuflisted modifiable 2504 let batchfile = '' 2505 try 2506 let [sh, shellcmdflag, shrd] = s:chsh(1) 2507 let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha 2508 if s:is_win 2509 let [batchfile, cmd] = s:batchfile(cmd) 2510 endif 2511 execute 'silent %!' cmd 2512 finally 2513 let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] 2514 if s:is_win && filereadable(batchfile) 2515 call delete(batchfile) 2516 endif 2517 endtry 2518 setlocal nomodifiable 2519 nnoremap <silent> <buffer> q :q<cr> 2520 wincmd p 2521endfunction 2522 2523function! s:section(flags) 2524 call search('\(^[x-] \)\@<=[^:]\+:', a:flags) 2525endfunction 2526 2527function! s:format_git_log(line) 2528 let indent = ' ' 2529 let tokens = split(a:line, nr2char(1)) 2530 if len(tokens) != 5 2531 return indent.substitute(a:line, '\s*$', '', '') 2532 endif 2533 let [graph, sha, refs, subject, date] = tokens 2534 let tag = matchstr(refs, 'tag: [^,)]\+') 2535 let tag = empty(tag) ? ' ' : ' ('.tag.') ' 2536 return printf('%s%s%s%s%s (%s)', indent, graph, sha, tag, subject, date) 2537endfunction 2538 2539function! s:append_ul(lnum, text) 2540 call append(a:lnum, ['', a:text, repeat('-', len(a:text))]) 2541endfunction 2542 2543function! s:diff() 2544 call s:prepare() 2545 call append(0, ['Collecting changes ...', '']) 2546 let cnts = [0, 0] 2547 let bar = '' 2548 let total = filter(copy(g:plugs), 's:is_managed(v:key) && isdirectory(v:val.dir)') 2549 call s:progress_bar(2, bar, len(total)) 2550 for origin in [1, 0] 2551 let plugs = reverse(sort(items(filter(copy(total), (origin ? '' : '!').'(has_key(v:val, "commit") || has_key(v:val, "tag"))')))) 2552 if empty(plugs) 2553 continue 2554 endif 2555 call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:') 2556 for [k, v] in plugs 2557 let range = origin ? '..origin/'.v.branch : 'HEAD@{1}..' 2558 let cmd = ['git', 'log', '--graph', '--color=never'] 2559 if s:git_version_requirement(2, 10, 0) 2560 call add(cmd, '--no-show-signature') 2561 endif 2562 call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range]) 2563 if has_key(v, 'rtp') 2564 call extend(cmd, ['--', v.rtp]) 2565 endif 2566 let diff = s:system_chomp(cmd, v.dir) 2567 if !empty(diff) 2568 let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' 2569 call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) 2570 let cnts[origin] += 1 2571 endif 2572 let bar .= '=' 2573 call s:progress_bar(2, bar, len(total)) 2574 normal! 2G 2575 redraw 2576 endfor 2577 if !cnts[origin] 2578 call append(5, ['', 'N/A']) 2579 endif 2580 endfor 2581 call setline(1, printf('%d plugin(s) updated.', cnts[0]) 2582 \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : '')) 2583 2584 if cnts[0] || cnts[1] 2585 nnoremap <silent> <buffer> <plug>(plug-preview) :silent! call <SID>preview_commit()<cr> 2586 if empty(maparg("\<cr>", 'n')) 2587 nmap <buffer> <cr> <plug>(plug-preview) 2588 endif 2589 if empty(maparg('o', 'n')) 2590 nmap <buffer> o <plug>(plug-preview) 2591 endif 2592 endif 2593 if cnts[0] 2594 nnoremap <silent> <buffer> X :call <SID>revert()<cr> 2595 echo "Press 'X' on each block to revert the update" 2596 endif 2597 normal! gg 2598 setlocal nomodifiable 2599endfunction 2600 2601function! s:revert() 2602 if search('^Pending updates', 'bnW') 2603 return 2604 endif 2605 2606 let name = s:find_name(line('.')) 2607 if empty(name) || !has_key(g:plugs, name) || 2608 \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y' 2609 return 2610 endif 2611 2612 call s:system('git reset --hard HEAD@{1} && git checkout '.plug#shellescape(g:plugs[name].branch).' --', g:plugs[name].dir) 2613 setlocal modifiable 2614 normal! "_dap 2615 setlocal nomodifiable 2616 echo 'Reverted' 2617endfunction 2618 2619function! s:snapshot(force, ...) abort 2620 call s:prepare() 2621 setf vim 2622 call append(0, ['" Generated by vim-plug', 2623 \ '" '.strftime("%c"), 2624 \ '" :source this file in vim to restore the snapshot', 2625 \ '" or execute: vim -S snapshot.vim', 2626 \ '', '', 'PlugUpdate!']) 2627 1 2628 let anchor = line('$') - 3 2629 let names = sort(keys(filter(copy(g:plugs), 2630 \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)'))) 2631 for name in reverse(names) 2632 let sha = s:system_chomp(['git', 'rev-parse', '--short', 'HEAD'], g:plugs[name].dir) 2633 if !empty(sha) 2634 call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) 2635 redraw 2636 endif 2637 endfor 2638 2639 if a:0 > 0 2640 let fn = s:plug_expand(a:1) 2641 if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) 2642 return 2643 endif 2644 call writefile(getline(1, '$'), fn) 2645 echo 'Saved as '.a:1 2646 silent execute 'e' s:esc(fn) 2647 setf vim 2648 endif 2649endfunction 2650 2651function! s:split_rtp() 2652 return split(&rtp, '\\\@<!,') 2653endfunction 2654 2655let s:first_rtp = s:escrtp(get(s:split_rtp(), 0, '')) 2656let s:last_rtp = s:escrtp(get(s:split_rtp(), -1, '')) 2657 2658if exists('g:plugs') 2659 let g:plugs_order = get(g:, 'plugs_order', keys(g:plugs)) 2660 call s:upgrade_specs() 2661 call s:define_commands() 2662endif 2663 2664let &cpo = s:cpo_save 2665unlet s:cpo_save