plug.vim 25 KB

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