plug.vim 26 KB

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