plug.vim 25 KB

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