plug.vim 25 KB

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