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