plug.vim 25 KB

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