plug.vim 25 KB

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