plug.vim 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908
  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) 2014 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_buf = -1
  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=+ -bar Plug call s:add(1, <args>)
  88. command! -nargs=* -complete=customlist,s:names PlugInstall call s:install(<f-args>)
  89. command! -nargs=* -complete=customlist,s:names 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! s:to_a(v)
  97. return type(a:v) == 3 ? a:v : [a:v]
  98. endfunction
  99. function! plug#end()
  100. if !exists('g:plugs')
  101. echoerr 'Call plug#begin() first'
  102. return
  103. endif
  104. let keys = keys(g:plugs)
  105. while !empty(keys)
  106. let keys = keys(s:extend(keys))
  107. endwhile
  108. if exists('#PlugLOD')
  109. augroup PlugLOD
  110. autocmd!
  111. augroup END
  112. augroup! PlugLOD
  113. endif
  114. filetype off
  115. " we want to make sure the plugin directories are added to rtp in the same
  116. " order that they are registered with the Plug command. since the s:add_rtp
  117. " function uses ^= to add plugin directories to the front of the rtp, we
  118. " need to loop through the plugins in reverse
  119. for name in reverse(copy(g:plugs_order))
  120. let plug = g:plugs[name]
  121. if !has_key(plug, 'on') && !has_key(plug, 'for')
  122. call s:add_rtp(s:rtp(plug))
  123. continue
  124. endif
  125. if has_key(plug, 'on')
  126. let commands = s:to_a(plug.on)
  127. for cmd in commands
  128. if cmd =~ '^<Plug>.\+'
  129. if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i'))
  130. for [mode, prefix] in [['i', "<C-O>"], ['', '']]
  131. execute printf(
  132. \ "%snoremap <silent> %s %s:call <SID>lod_map(%s, %s)<CR>",
  133. \ mode, cmd, prefix, string(cmd), string(plug))
  134. endfor
  135. endif
  136. elseif !exists(':'.cmd)
  137. execute printf(
  138. \ "command! -nargs=* -range -bang %s call s:lod_cmd(%s, '<bang>', <line1>, <line2>, <q-args>, %s)",
  139. \ cmd, string(cmd), string(plug))
  140. endif
  141. endfor
  142. endif
  143. if has_key(plug, 'for')
  144. for vim in split(globpath(s:rtp(plug), 'ftdetect/**/*.vim'), '\n')
  145. execute 'source '.vim
  146. endfor
  147. let pat = join(s:to_a(plug.for), ',')
  148. augroup PlugLOD
  149. execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s, %s)',
  150. \ pat, string(pat), string(name), string(plug))
  151. augroup END
  152. endif
  153. endfor
  154. filetype plugin indent on
  155. syntax on
  156. endfunction
  157. function! s:rtp(spec)
  158. let rtp = s:dirpath(a:spec.dir . get(a:spec, 'rtp', ''))
  159. if s:is_win
  160. let rtp = substitute(rtp, '\\*$', '', '')
  161. endif
  162. return rtp
  163. endfunction
  164. function! s:esc(path)
  165. return substitute(a:path, ' ', '\\ ', 'g')
  166. endfunction
  167. function! s:add_rtp(rtp)
  168. execute "set rtp^=".s:esc(a:rtp)
  169. if isdirectory(a:rtp.'after')
  170. execute "set rtp+=".s:esc(a:rtp.'after')
  171. endif
  172. endfunction
  173. function! s:lod(plug, types)
  174. let rtp = s:rtp(a:plug)
  175. call s:add_rtp(rtp)
  176. for dir in a:types
  177. for vim in split(globpath(rtp, dir.'/**/*.vim'), '\n')
  178. execute 'source '.vim
  179. endfor
  180. endfor
  181. endfunction
  182. function! s:lod_ft(pat, name, plug)
  183. call s:lod(a:plug, ['plugin', 'after'])
  184. execute 'autocmd! PlugLOD FileType ' . a:pat
  185. endfunction
  186. function! s:lod_cmd(cmd, bang, l1, l2, args, plug)
  187. execute 'delc '.a:cmd
  188. call s:lod(a:plug, ['plugin', 'ftdetect', 'after'])
  189. execute printf("%s%s%s %s", (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
  190. endfunction
  191. function! s:lod_map(map, plug)
  192. execute 'unmap '.a:map
  193. execute 'iunmap '.a:map
  194. call s:lod(a:plug, ['plugin', 'ftdetect', 'after'])
  195. let extra = ''
  196. while 1
  197. let c = getchar(0)
  198. if c == 0
  199. break
  200. endif
  201. let extra .= nr2char(c)
  202. endwhile
  203. call feedkeys(substitute(a:map, '^<Plug>', "\<Plug>", '') . extra)
  204. endfunction
  205. function! s:add(...)
  206. let force = a:1
  207. let opts = { 'branch': 'master' }
  208. if a:0 == 2
  209. let plugin = a:2
  210. elseif a:0 == 3
  211. let plugin = a:2
  212. if type(a:3) == 1
  213. let opts.branch = a:3
  214. elseif type(a:3) == 4
  215. call extend(opts, a:3)
  216. else
  217. echoerr "Invalid argument type (expected: string or dictionary)"
  218. return
  219. endif
  220. else
  221. echoerr "Invalid number of arguments (1..2)"
  222. return
  223. endif
  224. if plugin =~ ':'
  225. let uri = plugin
  226. else
  227. if plugin !~ '/'
  228. let plugin = 'vim-scripts/'. plugin
  229. endif
  230. let uri = 'https://git:@github.com/' . plugin . '.git'
  231. endif
  232. let name = substitute(split(plugin, '/')[-1], '\.git$', '', '')
  233. if !force && has_key(g:plugs, name) | return | endif
  234. let dir = s:dirpath( fnamemodify(join([g:plug_home, name], '/'), ':p') )
  235. let spec = extend(opts, { 'dir': dir, 'uri': uri })
  236. let g:plugs[name] = spec
  237. let g:plugs_order += [name]
  238. endfunction
  239. function! s:install(...)
  240. call s:update_impl(0, a:000)
  241. endfunction
  242. function! s:update(...)
  243. call s:update_impl(1, a:000)
  244. endfunction
  245. function! s:apply()
  246. for spec in values(g:plugs)
  247. let docd = join([spec.dir, 'doc'], '/')
  248. if isdirectory(docd)
  249. execute "helptags ". join([spec.dir, 'doc'], '/')
  250. endif
  251. endfor
  252. runtime! plugin/*.vim
  253. runtime! after/*.vim
  254. silent! source $MYVIMRC
  255. endfunction
  256. function! s:syntax()
  257. syntax clear
  258. syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber
  259. syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX
  260. syn match plugNumber /[0-9]\+[0-9.]*/ contained
  261. syn match plugBracket /[[\]]/ contained
  262. syn match plugX /x/ contained
  263. syn match plugDash /^-/
  264. syn match plugName /\(^- \)\@<=[^:]*/
  265. syn match plugCommit /^ [0-9a-z]\{7} .*/ contains=plugRelDate,plugSha
  266. syn match plugSha /\(^ \)\@<=[0-9a-z]\{7}/ contained
  267. syn match plugRelDate /([^)]*)$/ contained
  268. syn match plugError /^x.*/
  269. syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
  270. hi def link plug1 Title
  271. hi def link plug2 Repeat
  272. hi def link plugX Exception
  273. hi def link plugBracket Structure
  274. hi def link plugNumber Number
  275. hi def link plugDash Special
  276. hi def link plugName Label
  277. hi def link plugError Error
  278. hi def link plugRelDate Comment
  279. hi def link plugSha Identifier
  280. endfunction
  281. function! s:lpad(str, len)
  282. return a:str . repeat(' ', a:len - len(a:str))
  283. endfunction
  284. function! s:lastline(msg)
  285. let lines = split(a:msg, '\n')
  286. return get(lines, -1, '')
  287. endfunction
  288. function! s:prepare()
  289. if bufexists(s:plug_buf)
  290. let winnr = bufwinnr(s:plug_buf)
  291. if winnr < 0
  292. vertical topleft new
  293. execute 'buffer ' . s:plug_buf
  294. else
  295. execute winnr . 'wincmd w'
  296. endif
  297. silent %d _
  298. else
  299. vertical topleft new
  300. nnoremap <silent> <buffer> q :if b:plug_preview==1<bar>pc<bar>endif<bar>q<cr>
  301. nnoremap <silent> <buffer> D :PlugDiff<cr>
  302. nnoremap <silent> <buffer> S :PlugStatus<cr>
  303. nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr>
  304. nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr>
  305. let b:plug_preview = -1
  306. let s:plug_buf = winbufnr(0)
  307. call s:assign_name()
  308. endif
  309. silent! unmap <buffer> <cr>
  310. setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline
  311. setf vim-plug
  312. call s:syntax()
  313. endfunction
  314. function! s:assign_name()
  315. " Assign buffer name
  316. let prefix = '[Plugins]'
  317. let name = prefix
  318. let idx = 2
  319. while bufexists(name)
  320. let name = printf("%s (%s)", prefix, idx)
  321. let idx = idx + 1
  322. endwhile
  323. silent! execute "f ".fnameescape(name)
  324. endfunction
  325. function! s:finish(pull)
  326. call append(3, '- Finishing ... ')
  327. redraw
  328. call s:apply()
  329. call s:syntax()
  330. call setline(4, getline(4) . 'Done!')
  331. normal! gg
  332. redraw
  333. if a:pull
  334. echo "Press 'D' to see the updated changes."
  335. endif
  336. endfunction
  337. function! s:names(...)
  338. return filter(keys(g:plugs), 'stridx(v:val, a:1) == 0')
  339. endfunction
  340. function! s:update_impl(pull, args) abort
  341. let args = copy(a:args)
  342. let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
  343. \ remove(args, -1) : get(g:, 'plug_threads', 16)
  344. let todo = empty(args) ? g:plugs :
  345. \ filter(copy(g:plugs), 'index(args, v:key) >= 0')
  346. if empty(todo)
  347. echohl WarningMsg
  348. echo 'No plugin to '. (a:pull ? 'update' : 'install') . '.'
  349. echohl None
  350. return
  351. endif
  352. call s:prepare()
  353. call append(0, a:pull ? 'Updating plugins' : 'Installing plugins')
  354. call append(1, '['. s:lpad('', len(todo)) .']')
  355. normal! 2G
  356. redraw
  357. if has('ruby') && threads > 1
  358. call s:update_parallel(a:pull, todo, threads)
  359. else
  360. call s:update_serial(a:pull, todo)
  361. endif
  362. call s:finish(a:pull)
  363. endfunction
  364. function! s:extend(names)
  365. let prev = copy(g:plugs)
  366. try
  367. command! -nargs=+ Plug call s:add(0, <args>)
  368. for name in a:names
  369. let plugfile = s:rtp(g:plugs[name]) . s:plug_file
  370. if filereadable(plugfile)
  371. execute "source ". s:esc(plugfile)
  372. endif
  373. endfor
  374. finally
  375. command! -nargs=+ Plug call s:add(1, <args>)
  376. endtry
  377. return filter(copy(g:plugs), '!has_key(prev, v:key)')
  378. endfunction
  379. function! s:update_progress(pull, cnt, bar, total)
  380. call setline(1, (a:pull ? 'Updating' : 'Installing').
  381. \ " plugins (".a:cnt."/".a:total.")")
  382. call s:progress_bar(2, a:bar, a:total)
  383. normal! 2G
  384. redraw
  385. endfunction
  386. function! s:update_serial(pull, todo)
  387. let st = reltime()
  388. let base = g:plug_home
  389. let todo = copy(a:todo)
  390. let total = len(todo)
  391. let done = {}
  392. let bar = ''
  393. while !empty(todo)
  394. for [name, spec] in items(todo)
  395. let done[name] = 1
  396. if isdirectory(spec.dir)
  397. execute 'cd '.s:esc(spec.dir)
  398. let [valid, msg] = s:git_valid(spec, 0, 0)
  399. if valid
  400. let result = a:pull ?
  401. \ s:system(
  402. \ printf('git checkout -q %s 2>&1 && git pull origin %s 2>&1 && git submodule update --init --recursive 2>&1',
  403. \ s:shellesc(spec.branch), s:shellesc(spec.branch))) : 'Already installed'
  404. let error = a:pull ? v:shell_error != 0 : 0
  405. else
  406. let result = msg
  407. let error = 1
  408. endif
  409. else
  410. if !isdirectory(base)
  411. call mkdir(base, 'p')
  412. endif
  413. execute 'cd '.base
  414. let result = s:system(
  415. \ printf('git clone --recursive %s -b %s %s 2>&1 && cd %s && git submodule update --init --recursive 2>&1',
  416. \ s:shellesc(spec.uri),
  417. \ s:shellesc(spec.branch),
  418. \ s:shellesc(substitute(spec.dir, '[\/]\+$', '', '')),
  419. \ s:shellesc(spec.dir)))
  420. let error = v:shell_error != 0
  421. endif
  422. cd -
  423. let bar .= error ? 'x' : '='
  424. call append(3, s:format_message(!error, name, result))
  425. call s:update_progress(a:pull, len(done), bar, total)
  426. endfor
  427. if !empty(s:extend(keys(todo)))
  428. let todo = filter(copy(g:plugs), '!has_key(done, v:key)')
  429. let total += len(todo)
  430. call s:update_progress(a:pull, len(done), bar, total)
  431. else
  432. break
  433. endif
  434. endwhile
  435. call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
  436. endfunction
  437. function! s:update_parallel(pull, todo, threads)
  438. ruby << EOF
  439. def esc arg
  440. %["#{arg.gsub('"', '\"')}"]
  441. end
  442. st = Time.now
  443. require 'thread'
  444. require 'fileutils'
  445. require 'timeout'
  446. running = true
  447. iswin = VIM::evaluate('s:is_win').to_i == 1
  448. pull = VIM::evaluate('a:pull').to_i == 1
  449. base = VIM::evaluate('g:plug_home')
  450. all = VIM::evaluate('copy(a:todo)')
  451. limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
  452. nthr = VIM::evaluate('a:threads').to_i
  453. cd = iswin ? 'cd /d' : 'cd'
  454. done = {}
  455. tot = 0
  456. bar = ''
  457. skip = 'Already installed'
  458. mtx = Mutex.new
  459. take1 = proc { mtx.synchronize { running && all.shift } }
  460. logh = proc {
  461. cnt = done.length
  462. tot = VIM::evaluate('len(a:todo)') || tot
  463. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  464. $curbuf[2] = '[' + bar.ljust(tot) + ']'
  465. VIM::command('normal! 2G')
  466. VIM::command('redraw') unless iswin
  467. }
  468. log = proc { |name, result, ok|
  469. mtx.synchronize do
  470. bar += ok ? '=' : 'x'
  471. done[name] = true
  472. result =
  473. if ok
  474. ["- #{name}: #{result.lines.to_a.last.strip}"]
  475. elsif result =~ /^Interrupted|^Timeout/
  476. ["x #{name}: #{result}"]
  477. else
  478. ["x #{name}"] + result.lines.map { |l| " " << l }
  479. end
  480. result.each_with_index do |line, offset|
  481. $curbuf.append 3 + offset, line.chomp
  482. end
  483. logh.call
  484. end
  485. }
  486. bt = proc { |cmd|
  487. begin
  488. fd = nil
  489. Timeout::timeout(limit) do
  490. if iswin
  491. tmp = VIM::evaluate('tempname()')
  492. system("#{cmd} > #{tmp}")
  493. data = File.read(tmp).chomp
  494. File.unlink tmp rescue nil
  495. else
  496. fd = IO.popen(cmd)
  497. data = fd.read.chomp
  498. fd.close
  499. end
  500. [$? == 0, data]
  501. end
  502. rescue Timeout::Error, Interrupt => e
  503. if fd && !fd.closed?
  504. pids = [fd.pid]
  505. unless `which pgrep`.empty?
  506. children = pids
  507. until children.empty?
  508. children = children.map { |pid|
  509. `pgrep -P #{pid}`.lines.map(&:chomp)
  510. }.flatten
  511. pids += children
  512. end
  513. end
  514. pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
  515. fd.close
  516. end
  517. [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
  518. end
  519. }
  520. main = Thread.current
  521. threads = []
  522. watcher = Thread.new {
  523. while VIM::evaluate('getchar(1)')
  524. sleep 0.1
  525. end
  526. mtx.synchronize do
  527. running = false
  528. threads.each { |t| t.raise Interrupt }
  529. end
  530. threads.each { |t| t.join rescue nil }
  531. main.kill
  532. }
  533. until all.empty?
  534. names = all.keys
  535. [names.length, nthr].min.times do
  536. mtx.synchronize do
  537. threads << Thread.new {
  538. while pair = take1.call
  539. name = pair.first
  540. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  541. branch = esc branch
  542. ok, result =
  543. if File.directory? dir
  544. dir = esc dir
  545. ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url"
  546. current_uri = data.lines.to_a.last
  547. if !ret
  548. if data =~ /^Interrupted|^Timeout/
  549. [false, data]
  550. else
  551. [false, [data.chomp, "PlugClean required."].join($/)]
  552. end
  553. elsif current_uri.sub(/git:@/, '') != uri.sub(/git:@/, '')
  554. [false, ["Invalid URI: #{current_uri}",
  555. "Expected: #{uri}",
  556. "PlugClean required."].join($/)]
  557. else
  558. if pull
  559. bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && git pull origin #{branch} 2>&1 && git submodule update --init --recursive 2>&1"
  560. else
  561. [true, skip]
  562. end
  563. end
  564. else
  565. FileUtils.mkdir_p(base)
  566. d = esc dir.sub(%r{[\\/]+$}, '')
  567. bt.call "#{cd} #{base} && git clone --recursive #{uri} -b #{branch} #{d} 2>&1 && cd #{esc dir} && git submodule update --init --recursive 2>&1"
  568. end
  569. log.call name, result, ok
  570. end
  571. } if running
  572. end
  573. end
  574. threads.each(&:join)
  575. mtx.synchronize { threads.clear }
  576. all.merge!(VIM::evaluate("s:extend(#{names.inspect})") || {})
  577. logh.call
  578. end
  579. watcher.kill
  580. $curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
  581. EOF
  582. endfunction
  583. function! s:path(path)
  584. return substitute(s:is_win ? substitute(a:path, '/', '\', 'g') : a:path,
  585. \ '[/\\]*$', '', '')
  586. endfunction
  587. function! s:dirpath(path)
  588. let path = s:path(a:path)
  589. if s:is_win
  590. return path !~ '\\$' ? path.'\' : path
  591. else
  592. return path !~ '/$' ? path.'/' : path
  593. endif
  594. endfunction
  595. function! s:shellesc(arg)
  596. return '"'.substitute(a:arg, '"', '\\"', 'g').'"'
  597. endfunction
  598. function! s:glob_dir(path)
  599. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
  600. endfunction
  601. function! s:progress_bar(line, bar, total)
  602. call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
  603. endfunction
  604. function! s:compare_git_uri(a, b)
  605. let a = substitute(a:a, 'git:@', '', '')
  606. let b = substitute(a:b, 'git:@', '', '')
  607. return a ==# b
  608. endfunction
  609. function! s:format_message(ok, name, message)
  610. if a:ok
  611. return [printf('- %s: %s', a:name, s:lastline(a:message))]
  612. else
  613. let lines = map(split(a:message, '\n'), '" ".v:val')
  614. return extend([printf('x %s:', a:name)], lines)
  615. endif
  616. endfunction
  617. function! s:system(cmd)
  618. return system(s:is_win ? '('.a:cmd.')' : a:cmd)
  619. endfunction
  620. function! s:git_valid(spec, check_branch, cd)
  621. let ret = 1
  622. let msg = 'OK'
  623. if isdirectory(a:spec.dir)
  624. if a:cd | execute "cd " . s:esc(a:spec.dir) | endif
  625. let result = split(s:system("git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url"), '\n')
  626. let remote = result[-1]
  627. if v:shell_error != 0
  628. let msg = join([remote, "PlugClean required."], "\n")
  629. let ret = 0
  630. elseif !s:compare_git_uri(remote, a:spec.uri)
  631. let msg = join(['Invalid URI: '.remote,
  632. \ 'Expected: '.a:spec.uri,
  633. \ "PlugClean required."], "\n")
  634. let ret = 0
  635. elseif a:check_branch
  636. let branch = result[0]
  637. if a:spec.branch != branch
  638. let msg = 'Invalid branch: '.branch.'. Try PlugUpdate.'
  639. let ret = 0
  640. endif
  641. endif
  642. if a:cd | cd - | endif
  643. else
  644. let msg = 'Not found'
  645. let ret = 0
  646. endif
  647. return [ret, msg]
  648. endfunction
  649. function! s:clean(force)
  650. call s:prepare()
  651. call append(0, 'Searching for unused plugins in '.g:plug_home)
  652. call append(1, '')
  653. " List of valid directories
  654. let dirs = []
  655. let [cnt, total] = [0, len(g:plugs)]
  656. for spec in values(g:plugs)
  657. if s:git_valid(spec, 0, 1)[0]
  658. call add(dirs, spec.dir)
  659. endif
  660. let cnt += 1
  661. call s:progress_bar(2, repeat('=', cnt), total)
  662. normal! 2G
  663. redraw
  664. endfor
  665. let allowed = {}
  666. for dir in dirs
  667. let allowed[dir] = 1
  668. for child in s:glob_dir(dir)
  669. let allowed[child] = 1
  670. endfor
  671. endfor
  672. let todo = []
  673. let found = sort(s:glob_dir(g:plug_home))
  674. while !empty(found)
  675. let f = remove(found, 0)
  676. if !has_key(allowed, f) && isdirectory(f)
  677. call add(todo, f)
  678. call append(line('$'), '- ' . f)
  679. let found = filter(found, 'stridx(v:val, f) != 0')
  680. end
  681. endwhile
  682. normal! G
  683. redraw
  684. if empty(todo)
  685. call append(line('$'), 'Already clean.')
  686. else
  687. call inputsave()
  688. let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
  689. call inputrestore()
  690. if yes
  691. for dir in todo
  692. if isdirectory(dir)
  693. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(dir))
  694. endif
  695. endfor
  696. call append(line('$'), 'Removed.')
  697. else
  698. call append(line('$'), 'Cancelled.')
  699. endif
  700. endif
  701. normal! G
  702. endfunction
  703. function! s:upgrade()
  704. if executable('curl')
  705. let mee = s:shellesc(s:me)
  706. let new = s:shellesc(s:me . '.new')
  707. echo "Downloading ". s:plug_source
  708. redraw
  709. let mv = s:is_win ? 'move /Y' : 'mv -f'
  710. let cp = s:is_win ? 'copy /Y' : 'cp -f'
  711. call system(printf(
  712. \ "curl -fLo %s %s && ".cp." %s %s.old && ".mv." %s %s",
  713. \ new, s:plug_source, mee, mee, new, mee))
  714. if v:shell_error == 0
  715. unlet g:loaded_plug
  716. echo "Downloaded ". s:plug_source
  717. return 1
  718. else
  719. echoerr "Error upgrading vim-plug"
  720. return 0
  721. endif
  722. elseif has('ruby')
  723. echo "Downloading ". s:plug_source
  724. ruby << EOF
  725. require 'open-uri'
  726. require 'fileutils'
  727. me = VIM::evaluate('s:me')
  728. old = me + '.old'
  729. new = me + '.new'
  730. File.open(new, 'w') do |f|
  731. f << open(VIM::evaluate('s:plug_source')).read
  732. end
  733. FileUtils.cp me, old
  734. File.rename new, me
  735. EOF
  736. unlet g:loaded_plug
  737. echo "Downloaded ". s:plug_source
  738. return 1
  739. else
  740. echoerr "curl executable or ruby support not found"
  741. return 0
  742. endif
  743. endfunction
  744. function! s:status()
  745. call s:prepare()
  746. call append(0, 'Checking plugins')
  747. call append(1, '')
  748. let ecnt = 0
  749. let [cnt, total] = [0, len(g:plugs)]
  750. for [name, spec] in items(g:plugs)
  751. if isdirectory(spec.dir)
  752. let [valid, msg] = s:git_valid(spec, 1, 1)
  753. else
  754. let [valid, msg] = [0, 'Not found. Try PlugInstall.']
  755. endif
  756. let cnt += 1
  757. let ecnt += !valid
  758. call s:progress_bar(2, repeat('=', cnt), total)
  759. call append(3, s:format_message(valid, name, msg))
  760. normal! 2G
  761. redraw
  762. endfor
  763. call setline(1, 'Finished. '.ecnt.' error(s).')
  764. normal! gg
  765. endfunction
  766. function! s:is_preview_window_open()
  767. silent! wincmd P
  768. if &previewwindow
  769. wincmd p
  770. return 1
  771. endif
  772. return 0
  773. endfunction
  774. function! s:preview_commit()
  775. if b:plug_preview < 0
  776. let b:plug_preview = !s:is_preview_window_open()
  777. endif
  778. let sha = matchstr(getline('.'), '\(^ \)\@<=[0-9a-z]\{7}')
  779. if !empty(sha)
  780. let lnum = line('.')
  781. while lnum > 1
  782. let lnum -= 1
  783. let line = getline(lnum)
  784. let name = matchstr(line, '\(^- \)\@<=[^:]\+')
  785. if !empty(name)
  786. let dir = g:plugs[name].dir
  787. if isdirectory(dir)
  788. execute 'cd '.s:esc(dir)
  789. execute 'pedit '.sha
  790. wincmd P
  791. setlocal filetype=git buftype=nofile nobuflisted
  792. execute 'silent read !git show '.sha
  793. normal! ggdd
  794. wincmd p
  795. cd -
  796. endif
  797. break
  798. endif
  799. endwhile
  800. endif
  801. endfunction
  802. function! s:section(flags)
  803. call search('\(^- \)\@<=.', a:flags)
  804. endfunction
  805. function! s:diff()
  806. call s:prepare()
  807. call append(0, 'Collecting updated changes ...')
  808. normal! gg
  809. redraw
  810. let cnt = 0
  811. for [k, v] in items(g:plugs)
  812. if !isdirectory(v.dir)
  813. continue
  814. endif
  815. execute 'cd '.s:esc(v.dir)
  816. let diff = system('git log --pretty=format:"%h %s (%cr)" "HEAD@{0}...HEAD@{1}"')
  817. if !v:shell_error && !empty(diff)
  818. call append(1, '')
  819. call append(2, '- '.k.':')
  820. call append(3, map(split(diff, '\n'), '" ". v:val'))
  821. let cnt += 1
  822. normal! gg
  823. redraw
  824. endif
  825. cd -
  826. endfor
  827. call setline(1, cnt == 0 ? 'No updates.' : 'Last update:')
  828. nnoremap <silent> <buffer> <cr> :silent! call <SID>preview_commit()<cr>
  829. normal! gg
  830. endfunction
  831. let &cpo = s:cpo_save
  832. unlet s:cpo_save