plug.vim 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  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
  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=ALL
  234. syntax region plug2 start=/\%2l/ end=/\%3l/ contains=ALL
  235. syn match plugNumber /[0-9]\+[0-9.]*/ containedin=plug1 contained
  236. syn match plugBracket /[[\]]/ containedin=plug2 contained
  237. syn match plugX /x/ containedin=plug2 contained
  238. syn match plugDash /^-/
  239. syn match plugName /\(^- \)\@<=[^:]*/
  240. syn match plugRelDate /([^)/+-]\{3,})$/
  241. syn match plugSha /^ [0-9a-z]\{7}/
  242. syn match plugError /^x.*/
  243. syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
  244. hi def link plug1 Title
  245. hi def link plug2 Repeat
  246. hi def link plugX Exception
  247. hi def link plugBracket Structure
  248. hi def link plugNumber Number
  249. hi def link plugDash Special
  250. hi def link plugName Label
  251. hi def link plugError Error
  252. hi def link plugRelDate Comment
  253. hi def link plugSha Identifier
  254. endfunction
  255. function! s:lpad(str, len)
  256. return a:str . repeat(' ', a:len - len(a:str))
  257. endfunction
  258. function! s:lastline(msg)
  259. let lines = split(a:msg, '\n')
  260. return get(lines, -1, '')
  261. endfunction
  262. function! s:prepare()
  263. execute s:plug_win . 'wincmd w'
  264. if exists('b:plug')
  265. %d
  266. else
  267. vertical topleft new
  268. nnoremap <silent> <buffer> q :q<cr>
  269. nnoremap <silent> <buffer> D :PlugDiff<cr>
  270. nnoremap <silent> <buffer> S :PlugStatus<cr>
  271. let b:plug = 1
  272. let s:plug_win = winnr()
  273. call s:assign_name()
  274. endif
  275. setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline
  276. setf vim-plug
  277. call s:syntax()
  278. endfunction
  279. function! s:assign_name()
  280. " Assign buffer name
  281. let prefix = '[Plugins]'
  282. let name = prefix
  283. let idx = 2
  284. while bufexists(name)
  285. let name = printf("%s (%s)", prefix, idx)
  286. let idx = idx + 1
  287. endwhile
  288. silent! execute "f ".fnameescape(name)
  289. endfunction
  290. function! s:finish()
  291. call append(3, '- Finishing ... ')
  292. redraw
  293. call s:apply()
  294. call s:syntax()
  295. call setline(4, getline(4) . 'Done!')
  296. normal! gg
  297. endfunction
  298. function! s:update_impl(pull, args)
  299. let threads = len(a:args) > 0 ? a:args[0] : get(g:, 'plug_threads', 16)
  300. call s:prepare()
  301. call append(0, a:pull ? 'Updating plugins' : 'Installing plugins')
  302. call append(1, '['. s:lpad('', len(g:plugs)) .']')
  303. normal! 2G
  304. redraw
  305. if has('ruby') && threads > 1
  306. call s:update_parallel(a:pull, threads)
  307. else
  308. call s:update_serial(a:pull)
  309. endif
  310. call s:finish()
  311. endfunction
  312. function! s:extend(names)
  313. let prev = copy(g:plugs)
  314. try
  315. command! -nargs=+ Plug call s:add(0, <args>)
  316. for name in a:names
  317. let plugfile = s:rtp(g:plugs[name]) . s:plug_file
  318. if filereadable(plugfile)
  319. execute "source ". s:esc(plugfile)
  320. endif
  321. endfor
  322. finally
  323. command! -nargs=+ Plug call s:add(1, <args>)
  324. endtry
  325. return filter(copy(g:plugs), '!has_key(prev, v:key)')
  326. endfunction
  327. function! s:update_progress(pull, cnt, bar, total)
  328. call setline(1, (a:pull ? 'Updating' : 'Installing').
  329. \ " plugins (".a:cnt."/".a:total.")")
  330. call s:progress_bar(2, a:bar, a:total)
  331. normal! 2G
  332. redraw
  333. endfunction
  334. function! s:update_serial(pull)
  335. let st = reltime()
  336. let base = g:plug_home
  337. let todo = copy(g:plugs)
  338. let total = len(todo)
  339. let done = {}
  340. let bar = ''
  341. while !empty(todo)
  342. for [name, spec] in items(todo)
  343. let done[name] = 1
  344. if isdirectory(spec.dir)
  345. execute 'cd '.s:esc(spec.dir)
  346. let [valid, msg] = s:git_valid(spec, 0, 0)
  347. if valid
  348. let result = a:pull ?
  349. \ s:system(
  350. \ printf('git checkout -q %s 2>&1 && git pull origin %s 2>&1',
  351. \ s:shellesc(spec.branch), s:shellesc(spec.branch))) : 'Already installed'
  352. let error = a:pull ? v:shell_error != 0 : 0
  353. else
  354. let result = msg
  355. let error = 1
  356. endif
  357. else
  358. if !isdirectory(base)
  359. call mkdir(base, 'p')
  360. endif
  361. execute 'cd '.base
  362. let result = s:system(
  363. \ printf('git clone --recursive %s -b %s %s 2>&1',
  364. \ s:shellesc(spec.uri),
  365. \ s:shellesc(spec.branch),
  366. \ s:shellesc(substitute(spec.dir, '[\/]\+$', '', ''))))
  367. let error = v:shell_error != 0
  368. endif
  369. cd -
  370. let bar .= error ? 'x' : '='
  371. call append(3, s:format_message(!error, name, result))
  372. call s:update_progress(a:pull, len(done), bar, total)
  373. endfor
  374. if !empty(s:extend(keys(todo)))
  375. let todo = filter(copy(g:plugs), '!has_key(done, v:key)')
  376. let total += len(todo)
  377. call s:update_progress(a:pull, len(done), bar, total)
  378. else
  379. break
  380. endif
  381. endwhile
  382. call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
  383. endfunction
  384. function! s:update_parallel(pull, threads)
  385. ruby << EOF
  386. def esc arg
  387. %["#{arg.gsub('"', '\"')}"]
  388. end
  389. st = Time.now
  390. require 'thread'
  391. require 'fileutils'
  392. require 'timeout'
  393. running = true
  394. iswin = VIM::evaluate('s:is_win').to_i == 1
  395. pull = VIM::evaluate('a:pull').to_i == 1
  396. base = VIM::evaluate('g:plug_home')
  397. all = VIM::evaluate('copy(g:plugs)')
  398. limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
  399. nthr = VIM::evaluate('a:threads').to_i
  400. cd = iswin ? 'cd /d' : 'cd'
  401. done = {}
  402. tot = 0
  403. bar = ''
  404. skip = 'Already installed'
  405. mtx = Mutex.new
  406. take1 = proc { mtx.synchronize { running && all.shift } }
  407. logh = proc {
  408. cnt = done.length
  409. tot = VIM::evaluate('len(g:plugs)') || tot
  410. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  411. $curbuf[2] = '[' + bar.ljust(tot) + ']'
  412. VIM::command('normal! 2G')
  413. VIM::command('redraw') unless iswin
  414. }
  415. log = proc { |name, result, ok|
  416. mtx.synchronize do
  417. bar += ok ? '=' : 'x'
  418. done[name] = true
  419. result =
  420. if ok
  421. ["- #{name}: #{result.lines.to_a.last.strip}"]
  422. elsif result =~ /^Interrupted|^Timeout/
  423. ["x #{name}: #{result}"]
  424. else
  425. ["x #{name}"] + result.lines.map { |l| " " << l }
  426. end
  427. result.each_with_index do |line, offset|
  428. $curbuf.append 3 + offset, line.chomp
  429. end
  430. logh.call
  431. end
  432. }
  433. bt = proc { |cmd|
  434. begin
  435. fd = nil
  436. Timeout::timeout(limit) do
  437. if iswin
  438. tmp = VIM::evaluate('tempname()')
  439. system("#{cmd} > #{tmp}")
  440. data = File.read(tmp).chomp
  441. File.unlink tmp rescue nil
  442. else
  443. fd = IO.popen(cmd)
  444. data = fd.read.chomp
  445. fd.close
  446. end
  447. [$? == 0, data]
  448. end
  449. rescue Timeout::Error, Interrupt => e
  450. if fd && !fd.closed?
  451. pids = [fd.pid]
  452. unless `which pgrep`.empty?
  453. children = pids
  454. until children.empty?
  455. children = children.map { |pid|
  456. `pgrep -P #{pid}`.lines.map(&:chomp)
  457. }.flatten
  458. pids += children
  459. end
  460. end
  461. pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
  462. fd.close
  463. end
  464. [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
  465. end
  466. }
  467. main = Thread.current
  468. threads = []
  469. watcher = Thread.new {
  470. while VIM::evaluate('getchar(1)')
  471. sleep 0.1
  472. end
  473. mtx.synchronize do
  474. running = false
  475. threads.each { |t| t.raise Interrupt }
  476. end
  477. threads.each { |t| t.join rescue nil }
  478. main.kill
  479. }
  480. until all.empty?
  481. names = all.keys
  482. [names.length, nthr].min.times do
  483. mtx.synchronize do
  484. threads << Thread.new {
  485. while pair = take1.call
  486. name = pair.first
  487. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  488. branch = esc branch
  489. ok, result =
  490. if File.directory? dir
  491. dir = esc dir
  492. ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url"
  493. current_uri = data.lines.to_a.last
  494. if !ret
  495. if data =~ /^Interrupted|^Timeout/
  496. [false, data]
  497. else
  498. [false, [data.chomp, "PlugClean required."].join($/)]
  499. end
  500. elsif current_uri.sub(/git:@/, '') != uri.sub(/git:@/, '')
  501. [false, ["Invalid URI: #{current_uri}",
  502. "Expected: #{uri}",
  503. "PlugClean required."].join($/)]
  504. else
  505. if pull
  506. bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && git pull origin #{branch} 2>&1"
  507. else
  508. [true, skip]
  509. end
  510. end
  511. else
  512. FileUtils.mkdir_p(base)
  513. d = esc dir.sub(%r{[\\/]+$}, '')
  514. bt.call "#{cd} #{base} && git clone --recursive #{uri} -b #{branch} #{d} 2>&1"
  515. end
  516. log.call name, result, ok
  517. end
  518. } if running
  519. end
  520. end
  521. threads.each(&:join)
  522. mtx.synchronize { threads.clear }
  523. all.merge!(VIM::evaluate("s:extend(#{names.inspect})") || {})
  524. logh.call
  525. end
  526. watcher.kill
  527. $curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
  528. EOF
  529. endfunction
  530. function! s:path(path)
  531. return substitute(s:is_win ? substitute(a:path, '/', '\', 'g') : a:path,
  532. \ '[/\\]*$', '', '')
  533. endfunction
  534. function! s:dirpath(path)
  535. let path = s:path(a:path)
  536. if s:is_win
  537. return path !~ '\\$' ? path.'\' : path
  538. else
  539. return path !~ '/$' ? path.'/' : path
  540. endif
  541. endfunction
  542. function! s:shellesc(arg)
  543. return '"'.substitute(a:arg, '"', '\\"', 'g').'"'
  544. endfunction
  545. function! s:glob_dir(path)
  546. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
  547. endfunction
  548. function! s:progress_bar(line, bar, total)
  549. call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
  550. endfunction
  551. function! s:compare_git_uri(a, b)
  552. let a = substitute(a:a, 'git:@', '', '')
  553. let b = substitute(a:b, 'git:@', '', '')
  554. return a ==# b
  555. endfunction
  556. function! s:format_message(ok, name, message)
  557. if a:ok
  558. return [printf('- %s: %s', a:name, s:lastline(a:message))]
  559. else
  560. let lines = map(split(a:message, '\n'), '" ".v:val')
  561. return extend([printf('x %s:', a:name)], lines)
  562. endif
  563. endfunction
  564. function! s:system(cmd)
  565. return system(s:is_win ? '('.a:cmd.')' : a:cmd)
  566. endfunction
  567. function! s:git_valid(spec, check_branch, cd)
  568. let ret = 1
  569. let msg = 'OK'
  570. if isdirectory(a:spec.dir)
  571. if a:cd | execute "cd " . s:esc(a:spec.dir) | endif
  572. let result = split(s:system("git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url"), '\n')
  573. let remote = result[-1]
  574. if v:shell_error != 0
  575. let msg = join([remote, "PlugClean required."], "\n")
  576. let ret = 0
  577. elseif !s:compare_git_uri(remote, a:spec.uri)
  578. let msg = join(['Invalid URI: '.remote,
  579. \ 'Expected: '.a:spec.uri,
  580. \ "PlugClean required."], "\n")
  581. let ret = 0
  582. elseif a:check_branch
  583. let branch = result[0]
  584. if a:spec.branch != branch
  585. let msg = 'Invalid branch: '.branch.'. Try PlugUpdate.'
  586. let ret = 0
  587. endif
  588. endif
  589. if a:cd | cd - | endif
  590. else
  591. let msg = 'Not found'
  592. let ret = 0
  593. endif
  594. return [ret, msg]
  595. endfunction
  596. function! s:clean(force)
  597. call s:prepare()
  598. call append(0, 'Searching for unused plugins in '.g:plug_home)
  599. call append(1, '')
  600. " List of valid directories
  601. let dirs = []
  602. let [cnt, total] = [0, len(g:plugs)]
  603. for spec in values(g:plugs)
  604. if s:git_valid(spec, 0, 1)[0]
  605. call add(dirs, spec.dir)
  606. endif
  607. let cnt += 1
  608. call s:progress_bar(2, repeat('=', cnt), total)
  609. normal! 2G
  610. redraw
  611. endfor
  612. let allowed = {}
  613. for dir in dirs
  614. let allowed[dir] = 1
  615. for child in s:glob_dir(dir)
  616. let allowed[child] = 1
  617. endfor
  618. endfor
  619. let todo = []
  620. let found = sort(s:glob_dir(g:plug_home))
  621. while !empty(found)
  622. let f = remove(found, 0)
  623. if !has_key(allowed, f) && isdirectory(f)
  624. call add(todo, f)
  625. call append(line('$'), '- ' . f)
  626. let found = filter(found, 'stridx(v:val, f) != 0')
  627. end
  628. endwhile
  629. normal! G
  630. redraw
  631. if empty(todo)
  632. call append(line('$'), 'Already clean.')
  633. else
  634. call inputsave()
  635. let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
  636. call inputrestore()
  637. if yes
  638. for dir in todo
  639. if isdirectory(dir)
  640. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(dir))
  641. endif
  642. endfor
  643. call append(line('$'), 'Removed.')
  644. else
  645. call append(line('$'), 'Cancelled.')
  646. endif
  647. endif
  648. normal! G
  649. endfunction
  650. function! s:upgrade()
  651. if executable('curl')
  652. let mee = s:shellesc(s:me)
  653. let new = s:shellesc(s:me . '.new')
  654. echo "Downloading ". s:plug_source
  655. redraw
  656. let mv = s:is_win ? 'move /Y' : 'mv -f'
  657. let cp = s:is_win ? 'copy /Y' : 'cp -f'
  658. call system(printf(
  659. \ "curl -fLo %s %s && ".cp." %s %s.old && ".mv." %s %s",
  660. \ new, s:plug_source, mee, mee, new, mee))
  661. if v:shell_error == 0
  662. unlet g:loaded_plug
  663. echo "Downloaded ". s:plug_source
  664. return 1
  665. else
  666. echoerr "Error upgrading vim-plug"
  667. return 0
  668. endif
  669. elseif has('ruby')
  670. echo "Downloading ". s:plug_source
  671. ruby << EOF
  672. require 'open-uri'
  673. require 'fileutils'
  674. me = VIM::evaluate('s:me')
  675. old = me + '.old'
  676. new = me + '.new'
  677. File.open(new, 'w') do |f|
  678. f << open(VIM::evaluate('s:plug_source')).read
  679. end
  680. FileUtils.cp me, old
  681. File.rename new, me
  682. EOF
  683. unlet g:loaded_plug
  684. echo "Downloaded ". s:plug_source
  685. return 1
  686. else
  687. echoerr "curl executable or ruby support not found"
  688. return 0
  689. endif
  690. endfunction
  691. function! s:status()
  692. call s:prepare()
  693. call append(0, 'Checking plugins')
  694. call append(1, '')
  695. let ecnt = 0
  696. let [cnt, total] = [0, len(g:plugs)]
  697. for [name, spec] in items(g:plugs)
  698. if isdirectory(spec.dir)
  699. let [valid, msg] = s:git_valid(spec, 1, 1)
  700. else
  701. let [valid, msg] = [0, 'Not found. Try PlugInstall.']
  702. endif
  703. let cnt += 1
  704. let ecnt += !valid
  705. call s:progress_bar(2, repeat('=', cnt), total)
  706. call append(3, s:format_message(valid, name, msg))
  707. normal! 2G
  708. redraw
  709. endfor
  710. call setline(1, 'Finished. '.ecnt.' error(s).')
  711. normal! gg
  712. endfunction
  713. function! s:diff()
  714. call s:prepare()
  715. call append(0, 'Collecting updated changes ...')
  716. let cnt = 0
  717. for [k, v] in items(g:plugs)
  718. if !isdirectory(v.dir)
  719. continue
  720. endif
  721. execute 'cd '.substitute(v.dir, ' ', '\\ ', 'g')
  722. let diff = system('git log --pretty=format:"%h %s (%cr)" "HEAD@{0}...HEAD@{1}"')
  723. if !v:shell_error && !empty(diff)
  724. call append(1, '')
  725. call append(2, '- '.k.':')
  726. call append(3, map(split(diff, '\n'), '" ". v:val'))
  727. let cnt += 1
  728. normal! gg
  729. redraw
  730. endif
  731. cd -
  732. endfor
  733. if cnt == 0
  734. call setline(1, 'No update.')
  735. else
  736. call setline(1, 'Updated changes:')
  737. endif
  738. normal! gg
  739. endfunction
  740. let &cpo = s:cpo_save
  741. unlet s:cpo_save