plug.vim 22 KB

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