plug.vim 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. " vim-plug: Vim plugin manager
  2. " ============================
  3. "
  4. " Download plug.vim and put it in ~/.vim/autoload
  5. "
  6. " mkdir -p ~/.vim/autoload
  7. " curl -fLo ~/.vim/autoload/plug.vim \
  8. " https://raw.github.com/junegunn/vim-plug/master/plug.vim
  9. "
  10. " Edit your .vimrc
  11. "
  12. " call plug#begin()
  13. "
  14. " Plug 'junegunn/seoul256.vim'
  15. " Plug 'junegunn/vim-easy-align'
  16. " " Plug 'user/repo1', 'branch_or_tag'
  17. " " Plug 'user/repo2', { 'rtp': 'vim/plugin/dir', 'branch': 'devel' }
  18. " " ...
  19. "
  20. " call plug#end()
  21. "
  22. " Then :PlugInstall to install plugins. (default: ~/.vim/plugged)
  23. " You can change the location of the plugins with plug#begin(path) call.
  24. "
  25. "
  26. " Copyright (c) 2013 Junegunn Choi
  27. "
  28. " MIT License
  29. "
  30. " Permission is hereby granted, free of charge, to any person obtaining
  31. " a copy of this software and associated documentation files (the
  32. " "Software"), to deal in the Software without restriction, including
  33. " without limitation the rights to use, copy, modify, merge, publish,
  34. " distribute, sublicense, and/or sell copies of the Software, and to
  35. " permit persons to whom the Software is furnished to do so, subject to
  36. " the following conditions:
  37. "
  38. " The above copyright notice and this permission notice shall be
  39. " included in all copies or substantial portions of the Software.
  40. "
  41. " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  42. " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  43. " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  44. " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  45. " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  46. " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  47. " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  48. if exists('g:loaded_plug')
  49. finish
  50. endif
  51. let g:loaded_plug = 1
  52. let s:cpo_save = &cpo
  53. set cpo&vim
  54. let s:plug_source = 'https://raw.github.com/junegunn/vim-plug/master/plug.vim'
  55. let s:plug_file = 'Plugfile'
  56. let s:plug_win = 0
  57. let s:is_win = has('win32') || has('win64')
  58. let s:me = expand('<sfile>:p')
  59. function! plug#begin(...)
  60. let home = s:path(
  61. \ a:0 > 0 ? fnamemodify(a:1, ':p') :
  62. \ get(g:, 'plug_home', split(&rtp, ',')[0].'/plugged'))
  63. if !isdirectory(home)
  64. try
  65. call mkdir(home, 'p')
  66. catch
  67. echoerr 'Invalid plug directory: '. home
  68. return 0
  69. endtry
  70. endif
  71. if !executable('git')
  72. echoerr "`git' executable not found. vim-plug requires git."
  73. return 0
  74. endif
  75. let g:plug_home = home
  76. let g:plugs = {}
  77. command! -nargs=+ Plug call s:add(1, <args>)
  78. command! -nargs=* PlugInstall call s:install(<f-args>)
  79. command! -nargs=* PlugUpdate call s:update(<f-args>)
  80. command! -nargs=0 -bang PlugClean call s:clean('<bang>' == '!')
  81. command! -nargs=0 PlugUpgrade if s:upgrade() | execute "source ". s:me | endif
  82. command! -nargs=0 PlugStatus call s:status()
  83. return 1
  84. endfunction
  85. function! plug#end()
  86. let keys = keys(g:plugs)
  87. while !empty(keys)
  88. let keys = keys(s:extend(keys))
  89. endwhile
  90. filetype off
  91. for plug in values(g:plugs)
  92. let rtp = s:rtp(plug)
  93. execute "set rtp^=".rtp
  94. if isdirectory(rtp.'after')
  95. execute "set rtp+=".rtp.'after'
  96. endif
  97. endfor
  98. filetype plugin indent on
  99. syntax on
  100. endfunction
  101. function! s:rtp(spec)
  102. let rtp = s:dirpath(a:spec.dir . get(a:spec, 'rtp', ''))
  103. if s:is_win
  104. let rtp = substitute(rtp, '\\*$', '', '')
  105. endif
  106. return rtp
  107. endfunction
  108. function! s:add(...)
  109. let force = a:1
  110. let opts = { 'branch': 'master' }
  111. if a:0 == 2
  112. let plugin = a:2
  113. elseif a:0 == 3
  114. let plugin = a:2
  115. if type(a:3) == 1
  116. let opts.branch = a:3
  117. elseif type(a:3) == 4
  118. call extend(opts, a:3)
  119. else
  120. echoerr "Invalid argument type (expected: string or dictionary)"
  121. return
  122. endif
  123. else
  124. echoerr "Invalid number of arguments (1..2)"
  125. return
  126. endif
  127. if plugin =~ ':'
  128. let uri = plugin
  129. else
  130. if plugin !~ '/'
  131. let plugin = 'vim-scripts/'. plugin
  132. endif
  133. let uri = 'https://git:@github.com/' . plugin . '.git'
  134. endif
  135. let name = substitute(split(plugin, '/')[-1], '\.git$', '', '')
  136. if !force && has_key(g:plugs, name) | return | endif
  137. let dir = s:dirpath( fnamemodify(join([g:plug_home, name], '/'), ':p') )
  138. let spec = extend(opts, { 'dir': dir, 'uri': uri })
  139. let g:plugs[name] = spec
  140. endfunction
  141. function! s:install(...)
  142. call s:update_impl(0, a:000)
  143. endfunction
  144. function! s:update(...)
  145. call s:update_impl(1, a:000)
  146. endfunction
  147. function! s:apply()
  148. for spec in values(g:plugs)
  149. let docd = join([spec.dir, 'doc'], '/')
  150. if isdirectory(docd)
  151. execute "helptags ". join([spec.dir, 'doc'], '/')
  152. endif
  153. endfor
  154. runtime! plugin/*.vim
  155. runtime! after/*.vim
  156. silent! source $MYVIMRC
  157. endfunction
  158. function! s:syntax()
  159. syntax clear
  160. syntax region plug1 start=/\%1l/ end=/\%2l/ contains=ALL
  161. syntax region plug2 start=/\%2l/ end=/\%3l/ contains=ALL
  162. syn match plugNumber /[0-9]\+[0-9.]*/ containedin=plug1 contained
  163. syn match plugBracket /[[\]]/ containedin=plug2 contained
  164. syn match plugX /x/ containedin=plug2 contained
  165. syn match plugDash /^-/
  166. syn match plugName /\(^- \)\@<=[^:]*/
  167. syn match plugError /^x.*/
  168. syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
  169. hi def link plug1 Title
  170. hi def link plug2 Repeat
  171. hi def link plugX Exception
  172. hi def link plugBracket Structure
  173. hi def link plugNumber Number
  174. hi def link plugDash Special
  175. hi def link plugName Label
  176. hi def link plugError Error
  177. endfunction
  178. function! s:lpad(str, len)
  179. return a:str . repeat(' ', a:len - len(a:str))
  180. endfunction
  181. function! s:lastline(msg)
  182. let lines = split(a:msg, '\n')
  183. return get(lines, -1, '')
  184. endfunction
  185. function! s:prepare()
  186. execute s:plug_win . 'wincmd w'
  187. if exists('b:plug')
  188. %d
  189. else
  190. vertical topleft new
  191. noremap <silent> <buffer> q :q<cr>
  192. let b:plug = 1
  193. let s:plug_win = winnr()
  194. call s:assign_name()
  195. endif
  196. setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline
  197. setf vim-plug
  198. call s:syntax()
  199. endfunction
  200. function! s:assign_name()
  201. " Assign buffer name
  202. let prefix = '[Plugins]'
  203. let name = prefix
  204. let idx = 2
  205. while bufexists(name)
  206. let name = printf("%s (%s)", prefix, idx)
  207. let idx = idx + 1
  208. endwhile
  209. silent! execute "f ".fnameescape(name)
  210. endfunction
  211. function! s:finish()
  212. call append(3, '- Finishing ... ')
  213. redraw
  214. call s:apply()
  215. call s:syntax()
  216. call setline(4, getline(4) . 'Done!')
  217. normal! gg
  218. endfunction
  219. function! s:update_impl(pull, args)
  220. let threads = len(a:args) > 0 ? a:args[0] : get(g:, 'plug_threads', 16)
  221. call s:prepare()
  222. call append(0, a:pull ? 'Updating plugins' : 'Installing plugins')
  223. call append(1, '['. s:lpad('', len(g:plugs)) .']')
  224. normal! 2G
  225. redraw
  226. if has('ruby') && threads > 1
  227. call s:update_parallel(a:pull, threads)
  228. else
  229. call s:update_serial(a:pull)
  230. endif
  231. call s:finish()
  232. endfunction
  233. function! s:extend(names)
  234. let prev = copy(g:plugs)
  235. try
  236. command! -nargs=+ Plug call s:add(0, <args>)
  237. for name in a:names
  238. let plugfile = s:rtp(g:plugs[name]) . s:plug_file
  239. if filereadable(plugfile)
  240. execute "source ". plugfile
  241. endif
  242. endfor
  243. finally
  244. command! -nargs=+ Plug call s:add(1, <args>)
  245. endtry
  246. return filter(copy(g:plugs), '!has_key(prev, v:key)')
  247. endfunction
  248. function! s:update_progress(pull, cnt, bar, total)
  249. call setline(1, (a:pull ? 'Updating' : 'Installing').
  250. \ " plugins (".a:cnt."/".a:total.")")
  251. call s:progress_bar(2, a:bar, a:total)
  252. normal! 2G
  253. redraw
  254. endfunction
  255. function! s:update_serial(pull)
  256. let st = reltime()
  257. let base = g:plug_home
  258. let todo = copy(g:plugs)
  259. let total = len(todo)
  260. let done = {}
  261. let bar = ''
  262. while !empty(todo)
  263. for [name, spec] in items(todo)
  264. let done[name] = 1
  265. if isdirectory(spec.dir)
  266. execute 'cd '.spec.dir
  267. let [valid, msg] = s:git_valid(spec, 0, 0)
  268. if valid
  269. let result = a:pull ?
  270. \ system(
  271. \ printf('git checkout -q %s 2>&1 && git pull origin %s 2>&1',
  272. \ spec.branch, spec.branch)) : 'Already installed'
  273. let error = a:pull ? v:shell_error != 0 : 0
  274. else
  275. let result = msg
  276. let error = 1
  277. endif
  278. else
  279. if !isdirectory(base)
  280. call mkdir(base, 'p')
  281. endif
  282. execute 'cd '.base
  283. let d = shellescape(substitute(spec.dir, '[\/]\+$', '', ''))
  284. let result = system(
  285. \ printf('git clone --recursive %s -b %s %s 2>&1',
  286. \ shellescape(spec.uri), shellescape(spec.branch), d))
  287. let error = v:shell_error != 0
  288. endif
  289. cd -
  290. let bar .= error ? 'x' : '='
  291. call append(3, s:format_message(!error, name, result))
  292. call s:update_progress(a:pull, len(done), bar, total)
  293. endfor
  294. if !empty(s:extend(keys(todo)))
  295. let todo = filter(copy(g:plugs), '!has_key(done, v:key)')
  296. let total += len(todo)
  297. call s:update_progress(a:pull, len(done), bar, total)
  298. else
  299. break
  300. endif
  301. endwhile
  302. call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
  303. endfunction
  304. function! s:update_parallel(pull, threads)
  305. ruby << EOF
  306. st = Time.now
  307. require 'thread'
  308. require 'fileutils'
  309. require 'timeout'
  310. running = true
  311. iswin = VIM::evaluate('s:is_win').to_i == 1
  312. pull = VIM::evaluate('a:pull').to_i == 1
  313. base = VIM::evaluate('g:plug_home')
  314. all = VIM::evaluate('copy(g:plugs)')
  315. limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
  316. nthr = VIM::evaluate('a:threads').to_i
  317. cd = iswin ? 'cd /d' : 'cd'
  318. done = {}
  319. tot = 0
  320. bar = ''
  321. skip = 'Already installed'
  322. mtx = Mutex.new
  323. take1 = proc { mtx.synchronize { running && all.shift } }
  324. logh = proc {
  325. cnt = done.length
  326. tot = VIM::evaluate('len(g:plugs)') || tot
  327. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  328. $curbuf[2] = '[' + bar.ljust(tot) + ']'
  329. VIM::command('normal! 2G')
  330. VIM::command('redraw') unless iswin
  331. }
  332. log = proc { |name, result, ok|
  333. mtx.synchronize do
  334. bar += ok ? '=' : 'x'
  335. done[name] = true
  336. result =
  337. if ok
  338. ["- #{name}: #{result.lines.to_a.last.strip}"]
  339. elsif result =~ /^Interrupted|^Timeout/
  340. ["x #{name}: #{result}"]
  341. else
  342. ["x #{name}"] + result.lines.map { |l| " " << l }
  343. end
  344. result.each_with_index do |line, offset|
  345. $curbuf.append 3 + offset, line.chomp
  346. end
  347. logh.call
  348. end
  349. }
  350. bt = proc { |cmd|
  351. begin
  352. fd = nil
  353. Timeout::timeout(limit) do
  354. if iswin
  355. tmp = VIM::evaluate('tempname()')
  356. system("#{cmd} > #{tmp}")
  357. data = File.read(tmp).chomp
  358. File.unlink tmp rescue nil
  359. else
  360. fd = IO.popen(cmd)
  361. data = fd.read.chomp
  362. fd.close
  363. end
  364. [$? == 0, data]
  365. end
  366. rescue Timeout::Error, Interrupt => e
  367. if fd && !fd.closed?
  368. pids = [fd.pid]
  369. unless `which pgrep`.empty?
  370. children = pids
  371. until children.empty?
  372. children = children.map { |pid|
  373. `pgrep -P #{pid}`.lines.map(&:chomp)
  374. }.flatten
  375. pids += children
  376. end
  377. end
  378. pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
  379. fd.close
  380. end
  381. [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
  382. end
  383. }
  384. main = Thread.current
  385. threads = []
  386. watcher = Thread.new {
  387. while VIM::evaluate('getchar(1)')
  388. sleep 0.1
  389. end
  390. mtx.synchronize do
  391. running = false
  392. threads.each { |t| t.raise Interrupt }
  393. end
  394. threads.each { |t| t.join rescue nil }
  395. main.kill
  396. }
  397. until all.empty?
  398. names = all.keys
  399. [names.length, nthr].min.times do
  400. mtx.synchronize do
  401. threads << Thread.new {
  402. while pair = take1.call
  403. name = pair.first
  404. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  405. ok, result =
  406. if File.directory? dir
  407. ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url"
  408. current_uri = data.lines.to_a.last
  409. if !ret
  410. if data =~ /^Interrupted|^Timeout/
  411. [false, data]
  412. else
  413. [false, [data.chomp, "PlugClean required."].join($/)]
  414. end
  415. elsif current_uri.sub(/git:@/, '') != uri.sub(/git:@/, '')
  416. [false, ["Invalid URI: #{current_uri}",
  417. "Expected: #{uri}",
  418. "PlugClean required."].join($/)]
  419. else
  420. if pull
  421. bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && git pull origin #{branch} 2>&1"
  422. else
  423. [true, skip]
  424. end
  425. end
  426. else
  427. FileUtils.mkdir_p(base)
  428. d = dir.sub(%r{[\\/]+$}, '')
  429. bt.call "#{cd} #{base} && git clone --recursive #{uri} -b #{branch} #{d} 2>&1"
  430. end
  431. log.call name, result, ok
  432. end
  433. } if running
  434. end
  435. end
  436. threads.each(&:join)
  437. mtx.synchronize { threads.clear }
  438. all.merge!(VIM::evaluate("s:extend(#{names.inspect})") || {})
  439. logh.call
  440. end
  441. watcher.kill
  442. $curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
  443. EOF
  444. endfunction
  445. function! s:path(path)
  446. return substitute(s:is_win ? substitute(a:path, '/', '\', 'g') : a:path,
  447. \ '[/\\]*$', '', '')
  448. endfunction
  449. function! s:dirpath(path)
  450. let path = s:path(a:path)
  451. if s:is_win
  452. return path !~ '\\$' ? path.'\' : path
  453. else
  454. return path !~ '/$' ? path.'/' : path
  455. endif
  456. endfunction
  457. function! s:glob_dir(path)
  458. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
  459. endfunction
  460. function! s:progress_bar(line, bar, total)
  461. call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
  462. endfunction
  463. function! s:compare_git_uri(a, b)
  464. let a = substitute(a:a, 'git:@', '', '')
  465. let b = substitute(a:b, 'git:@', '', '')
  466. return a ==# b
  467. endfunction
  468. function! s:format_message(ok, name, message)
  469. if a:ok
  470. return [printf('- %s: %s', a:name, s:lastline(a:message))]
  471. else
  472. let lines = map(split(a:message, '\n'), '" ".v:val')
  473. return extend([printf('x %s:', a:name)], lines)
  474. endif
  475. endfunction
  476. function! s:git_valid(spec, check_branch, cd)
  477. let ret = 1
  478. let msg = 'OK'
  479. if isdirectory(a:spec.dir)
  480. if a:cd | execute "cd " . a:spec.dir | endif
  481. let result = split(system("git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url"), '\n')
  482. let remote = result[-1]
  483. if v:shell_error != 0
  484. let msg = join([remote, "PlugClean required."], "\n")
  485. let ret = 0
  486. elseif !s:compare_git_uri(remote, a:spec.uri)
  487. let msg = join(['Invalid URI: '.remote,
  488. \ 'Expected: '.a:spec.uri,
  489. \ "PlugClean required."], "\n")
  490. let ret = 0
  491. elseif a:check_branch
  492. let branch = result[0]
  493. if a:spec.branch != branch
  494. let msg = 'Invalid branch: '.branch.'. Try PlugUpdate.'
  495. let ret = 0
  496. endif
  497. endif
  498. if a:cd | cd - | endif
  499. else
  500. let msg = 'Not found'
  501. let ret = 0
  502. endif
  503. return [ret, msg]
  504. endfunction
  505. function! s:clean(force)
  506. call s:prepare()
  507. call append(0, 'Searching for unused plugins in '.g:plug_home)
  508. call append(1, '')
  509. " List of valid directories
  510. let dirs = []
  511. let [cnt, total] = [0, len(g:plugs)]
  512. for spec in values(g:plugs)
  513. if s:git_valid(spec, 0, 1)[0]
  514. call add(dirs, spec.dir)
  515. endif
  516. let cnt += 1
  517. call s:progress_bar(2, repeat('=', cnt), total)
  518. redraw
  519. endfor
  520. let allowed = {}
  521. for dir in dirs
  522. let allowed[dir] = 1
  523. for child in s:glob_dir(dir)
  524. let allowed[child] = 1
  525. endfor
  526. endfor
  527. let todo = []
  528. let found = sort(s:glob_dir(g:plug_home))
  529. while !empty(found)
  530. let f = remove(found, 0)
  531. if !has_key(allowed, f) && isdirectory(f)
  532. call add(todo, f)
  533. call append(line('$'), '- ' . f)
  534. let found = filter(found, 'stridx(v:val, f) != 0')
  535. end
  536. endwhile
  537. normal! G
  538. redraw
  539. if empty(todo)
  540. call append(line('$'), 'Already clean.')
  541. else
  542. call inputsave()
  543. let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
  544. call inputrestore()
  545. if yes
  546. for dir in todo
  547. if isdirectory(dir)
  548. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . dir)
  549. endif
  550. endfor
  551. call append(line('$'), 'Removed.')
  552. else
  553. call append(line('$'), 'Cancelled.')
  554. endif
  555. endif
  556. normal! G
  557. endfunction
  558. function! s:upgrade()
  559. if executable('curl')
  560. let mee = shellescape(s:me)
  561. let new = shellescape(s:me . '.new')
  562. echo "Downloading ". s:plug_source
  563. redraw
  564. let mv = s:is_win ? 'move /Y' : 'mv -f'
  565. let cp = s:is_win ? 'copy /Y' : 'cp -f'
  566. call system(printf(
  567. \ "curl -fLo %s %s && ".cp." %s %s.old && ".mv." %s %s",
  568. \ new, s:plug_source, mee, mee, new, mee))
  569. if v:shell_error == 0
  570. unlet g:loaded_plug
  571. echo "Downloaded ". s:plug_source
  572. return 1
  573. else
  574. echoerr "Error upgrading vim-plug"
  575. return 0
  576. endif
  577. elseif has('ruby')
  578. echo "Downloading ". s:plug_source
  579. ruby << EOF
  580. require 'open-uri'
  581. require 'fileutils'
  582. me = VIM::evaluate('s:me')
  583. old = me + '.old'
  584. new = me + '.new'
  585. File.open(new, 'w') do |f|
  586. f << open(VIM::evaluate('s:plug_source')).read
  587. end
  588. FileUtils.cp me, old
  589. File.rename new, me
  590. EOF
  591. unlet g:loaded_plug
  592. echo "Downloaded ". s:plug_source
  593. return 1
  594. else
  595. echoerr "curl executable or ruby support not found"
  596. return 0
  597. endif
  598. endfunction
  599. function! s:status()
  600. call s:prepare()
  601. call append(0, 'Checking plugins')
  602. let ecnt = 0
  603. for [name, spec] in items(g:plugs)
  604. if isdirectory(spec.dir)
  605. execute 'cd '.spec.dir
  606. let [valid, msg] = s:git_valid(spec, 1, 0)
  607. cd -
  608. else
  609. let [valid, msg] = [0, 'Not found. Try PlugInstall.']
  610. endif
  611. let ecnt += !valid
  612. call append(2, s:format_message(valid, name, msg))
  613. call cursor(3, 1)
  614. redraw
  615. endfor
  616. call setline(1, 'Finished. '.ecnt.' error(s).')
  617. normal! gg
  618. endfunction
  619. let &cpo = s:cpo_save
  620. unlet s:cpo_save