plug.vim 18 KB

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