plug.vim 26 KB

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