plug.vim 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933
  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. let lod = {}
  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. for key in s:to_a(plug.for)
  149. if !has_key(lod, key)
  150. let lod[key] = []
  151. endif
  152. call add(lod[key], name)
  153. endfor
  154. endif
  155. endfor
  156. for [key, names] in items(lod)
  157. augroup PlugLOD
  158. execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s)',
  159. \ key, string(key), string(reverse(names)))
  160. augroup END
  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, names)
  191. for name in a:names
  192. call s:lod(g:plugs[name], ['plugin', 'after'])
  193. endfor
  194. execute 'autocmd! PlugLOD FileType ' . a:pat
  195. let &l:filetype = &l:filetype
  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. let len = len(g:plugs)
  369. if has('ruby') && threads > 1
  370. call s:update_parallel(a:pull, todo, threads)
  371. else
  372. call s:update_serial(a:pull, todo)
  373. endif
  374. if len(g:plugs) > len
  375. call plug#end()
  376. endif
  377. call s:finish(a:pull)
  378. endfunction
  379. function! s:extend(names)
  380. let prev = copy(g:plugs)
  381. try
  382. command! -nargs=+ Plug call s:add(0, <args>)
  383. for name in a:names
  384. let plugfile = s:rtp(g:plugs[name]) . s:plug_file
  385. if filereadable(plugfile)
  386. execute "source ". s:esc(plugfile)
  387. endif
  388. endfor
  389. finally
  390. command! -nargs=+ Plug call s:add(1, <args>)
  391. endtry
  392. return filter(copy(g:plugs), '!has_key(prev, v:key)')
  393. endfunction
  394. function! s:update_progress(pull, cnt, bar, total)
  395. call setline(1, (a:pull ? 'Updating' : 'Installing').
  396. \ " plugins (".a:cnt."/".a:total.")")
  397. call s:progress_bar(2, a:bar, a:total)
  398. normal! 2G
  399. redraw
  400. endfunction
  401. function! s:update_serial(pull, todo)
  402. let st = reltime()
  403. let base = g:plug_home
  404. let todo = copy(a:todo)
  405. let total = len(todo)
  406. let done = {}
  407. let bar = ''
  408. while !empty(todo)
  409. for [name, spec] in items(todo)
  410. let done[name] = 1
  411. if isdirectory(spec.dir)
  412. execute 'cd '.s:esc(spec.dir)
  413. let [valid, msg] = s:git_valid(spec, 0, 0)
  414. if valid
  415. let result = a:pull ?
  416. \ s:system(
  417. \ printf('git checkout -q %s 2>&1 && git pull origin %s 2>&1 && git submodule update --init --recursive 2>&1',
  418. \ s:shellesc(spec.branch), s:shellesc(spec.branch))) : 'Already installed'
  419. let error = a:pull ? v:shell_error != 0 : 0
  420. else
  421. let result = msg
  422. let error = 1
  423. endif
  424. else
  425. if !isdirectory(base)
  426. call mkdir(base, 'p')
  427. endif
  428. execute 'cd '.base
  429. let result = s:system(
  430. \ printf('git clone --recursive %s -b %s %s 2>&1 && cd %s && git submodule update --init --recursive 2>&1',
  431. \ s:shellesc(spec.uri),
  432. \ s:shellesc(spec.branch),
  433. \ s:shellesc(substitute(spec.dir, '[\/]\+$', '', '')),
  434. \ s:shellesc(spec.dir)))
  435. let error = v:shell_error != 0
  436. endif
  437. cd -
  438. let bar .= error ? 'x' : '='
  439. call append(3, s:format_message(!error, name, result))
  440. call s:update_progress(a:pull, len(done), bar, total)
  441. endfor
  442. if !empty(s:extend(keys(todo)))
  443. let todo = filter(copy(g:plugs), '!has_key(done, v:key)')
  444. let total += len(todo)
  445. call s:update_progress(a:pull, len(done), bar, total)
  446. else
  447. break
  448. endif
  449. endwhile
  450. call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
  451. endfunction
  452. function! s:update_parallel(pull, todo, threads)
  453. ruby << EOF
  454. def esc arg
  455. %["#{arg.gsub('"', '\"')}"]
  456. end
  457. st = Time.now
  458. require 'thread'
  459. require 'fileutils'
  460. require 'timeout'
  461. running = true
  462. iswin = VIM::evaluate('s:is_win').to_i == 1
  463. pull = VIM::evaluate('a:pull').to_i == 1
  464. base = VIM::evaluate('g:plug_home')
  465. all = VIM::evaluate('copy(a:todo)')
  466. limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
  467. nthr = VIM::evaluate('a:threads').to_i
  468. cd = iswin ? 'cd /d' : 'cd'
  469. done = {}
  470. tot = 0
  471. bar = ''
  472. skip = 'Already installed'
  473. mtx = Mutex.new
  474. take1 = proc { mtx.synchronize { running && all.shift } }
  475. logh = proc {
  476. cnt = done.length
  477. tot = VIM::evaluate('len(a:todo)') || tot
  478. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  479. $curbuf[2] = '[' + bar.ljust(tot) + ']'
  480. VIM::command('normal! 2G')
  481. VIM::command('redraw') unless iswin
  482. }
  483. log = proc { |name, result, ok|
  484. mtx.synchronize do
  485. bar += ok ? '=' : 'x'
  486. done[name] = true
  487. result =
  488. if ok
  489. ["- #{name}: #{result.lines.to_a.last.strip}"]
  490. elsif result =~ /^Interrupted|^Timeout/
  491. ["x #{name}: #{result}"]
  492. else
  493. ["x #{name}"] + result.lines.map { |l| " " << l }
  494. end
  495. result.each_with_index do |line, offset|
  496. $curbuf.append 3 + offset, line.chomp
  497. end
  498. logh.call
  499. end
  500. }
  501. bt = proc { |cmd|
  502. begin
  503. fd = nil
  504. Timeout::timeout(limit) do
  505. if iswin
  506. tmp = VIM::evaluate('tempname()')
  507. system("#{cmd} > #{tmp}")
  508. data = File.read(tmp).chomp
  509. File.unlink tmp rescue nil
  510. else
  511. fd = IO.popen(cmd)
  512. data = fd.read.chomp
  513. fd.close
  514. end
  515. [$? == 0, data]
  516. end
  517. rescue Timeout::Error, Interrupt => e
  518. if fd && !fd.closed?
  519. pids = [fd.pid]
  520. unless `which pgrep`.empty?
  521. children = pids
  522. until children.empty?
  523. children = children.map { |pid|
  524. `pgrep -P #{pid}`.lines.map(&:chomp)
  525. }.flatten
  526. pids += children
  527. end
  528. end
  529. pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
  530. fd.close
  531. end
  532. [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
  533. end
  534. }
  535. main = Thread.current
  536. threads = []
  537. watcher = Thread.new {
  538. while VIM::evaluate('getchar(1)')
  539. sleep 0.1
  540. end
  541. mtx.synchronize do
  542. running = false
  543. threads.each { |t| t.raise Interrupt }
  544. end
  545. threads.each { |t| t.join rescue nil }
  546. main.kill
  547. }
  548. until all.empty?
  549. names = all.keys
  550. [names.length, nthr].min.times do
  551. mtx.synchronize do
  552. threads << Thread.new {
  553. while pair = take1.call
  554. name = pair.first
  555. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  556. branch = esc branch
  557. ok, result =
  558. if File.directory? dir
  559. dir = esc dir
  560. ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url"
  561. current_uri = data.lines.to_a.last
  562. if !ret
  563. if data =~ /^Interrupted|^Timeout/
  564. [false, data]
  565. else
  566. [false, [data.chomp, "PlugClean required."].join($/)]
  567. end
  568. elsif current_uri.sub(/git:@/, '') != uri.sub(/git:@/, '')
  569. [false, ["Invalid URI: #{current_uri}",
  570. "Expected: #{uri}",
  571. "PlugClean required."].join($/)]
  572. else
  573. if pull
  574. bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && git pull origin #{branch} 2>&1 && git submodule update --init --recursive 2>&1"
  575. else
  576. [true, skip]
  577. end
  578. end
  579. else
  580. FileUtils.mkdir_p(base)
  581. d = esc dir.sub(%r{[\\/]+$}, '')
  582. bt.call "#{cd} #{base} && git clone --recursive #{uri} -b #{branch} #{d} 2>&1 && cd #{esc dir} && git submodule update --init --recursive 2>&1"
  583. end
  584. log.call name, result, ok
  585. end
  586. } if running
  587. end
  588. end
  589. threads.each(&:join)
  590. mtx.synchronize { threads.clear }
  591. all.merge!(VIM::evaluate("s:extend(#{names.inspect})") || {})
  592. logh.call
  593. end
  594. watcher.kill
  595. $curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
  596. EOF
  597. endfunction
  598. function! s:path(path)
  599. return substitute(s:is_win ? substitute(a:path, '/', '\', 'g') : a:path,
  600. \ '[/\\]*$', '', '')
  601. endfunction
  602. function! s:dirpath(path)
  603. let path = s:path(a:path)
  604. if s:is_win
  605. return path !~ '\\$' ? path.'\' : path
  606. else
  607. return path !~ '/$' ? path.'/' : path
  608. endif
  609. endfunction
  610. function! s:shellesc(arg)
  611. return '"'.substitute(a:arg, '"', '\\"', 'g').'"'
  612. endfunction
  613. function! s:glob_dir(path)
  614. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
  615. endfunction
  616. function! s:progress_bar(line, bar, total)
  617. call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
  618. endfunction
  619. function! s:compare_git_uri(a, b)
  620. let a = substitute(a:a, 'git:@', '', '')
  621. let b = substitute(a:b, 'git:@', '', '')
  622. return a ==# b
  623. endfunction
  624. function! s:format_message(ok, name, message)
  625. if a:ok
  626. return [printf('- %s: %s', a:name, s:lastline(a:message))]
  627. else
  628. let lines = map(split(a:message, '\n'), '" ".v:val')
  629. return extend([printf('x %s:', a:name)], lines)
  630. endif
  631. endfunction
  632. function! s:system(cmd)
  633. return system(s:is_win ? '('.a:cmd.')' : a:cmd)
  634. endfunction
  635. function! s:system_chomp(str)
  636. let ret = s:system(a:str)
  637. return v:shell_error ? '' : substitute(ret, '\n$', '', '')
  638. endfunction
  639. function! s:git_valid(spec, check_branch, cd)
  640. let ret = 1
  641. let msg = 'OK'
  642. if isdirectory(a:spec.dir)
  643. if a:cd | execute "cd " . s:esc(a:spec.dir) | endif
  644. let result = split(s:system("git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url"), '\n')
  645. let remote = result[-1]
  646. if v:shell_error
  647. let msg = join([remote, "PlugClean required."], "\n")
  648. let ret = 0
  649. elseif !s:compare_git_uri(remote, a:spec.uri)
  650. let msg = join(['Invalid URI: '.remote,
  651. \ 'Expected: '.a:spec.uri,
  652. \ "PlugClean required."], "\n")
  653. let ret = 0
  654. elseif a:check_branch
  655. let branch = result[0]
  656. if a:spec.branch !=# branch
  657. let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1')
  658. if a:spec.branch !=# tag
  659. let msg = printf('Invalid branch/tag: %s (expected: %s). Try PlugUpdate.',
  660. \ (empty(tag) ? branch : tag), a:spec.branch)
  661. let ret = 0
  662. endif
  663. endif
  664. endif
  665. if a:cd | cd - | endif
  666. else
  667. let msg = 'Not found'
  668. let ret = 0
  669. endif
  670. return [ret, msg]
  671. endfunction
  672. function! s:clean(force)
  673. call s:prepare()
  674. call append(0, 'Searching for unused plugins in '.g:plug_home)
  675. call append(1, '')
  676. " List of valid directories
  677. let dirs = []
  678. let [cnt, total] = [0, len(g:plugs)]
  679. for spec in values(g:plugs)
  680. if s:git_valid(spec, 0, 1)[0]
  681. call add(dirs, spec.dir)
  682. endif
  683. let cnt += 1
  684. call s:progress_bar(2, repeat('=', cnt), total)
  685. normal! 2G
  686. redraw
  687. endfor
  688. let allowed = {}
  689. for dir in dirs
  690. let allowed[dir] = 1
  691. for child in s:glob_dir(dir)
  692. let allowed[child] = 1
  693. endfor
  694. endfor
  695. let todo = []
  696. let found = sort(s:glob_dir(g:plug_home))
  697. while !empty(found)
  698. let f = remove(found, 0)
  699. if !has_key(allowed, f) && isdirectory(f)
  700. call add(todo, f)
  701. call append(line('$'), '- ' . f)
  702. let found = filter(found, 'stridx(v:val, f) != 0')
  703. end
  704. endwhile
  705. normal! G
  706. redraw
  707. if empty(todo)
  708. call append(line('$'), 'Already clean.')
  709. else
  710. call inputsave()
  711. let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
  712. call inputrestore()
  713. if yes
  714. for dir in todo
  715. if isdirectory(dir)
  716. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(dir))
  717. endif
  718. endfor
  719. call append(line('$'), 'Removed.')
  720. else
  721. call append(line('$'), 'Cancelled.')
  722. endif
  723. endif
  724. normal! G
  725. endfunction
  726. function! s:upgrade()
  727. if executable('curl')
  728. let mee = s:shellesc(s:me)
  729. let new = s:shellesc(s:me . '.new')
  730. echo "Downloading ". s:plug_source
  731. redraw
  732. let mv = s:is_win ? 'move /Y' : 'mv -f'
  733. let cp = s:is_win ? 'copy /Y' : 'cp -f'
  734. call system(printf(
  735. \ "curl -fLo %s %s && ".cp." %s %s.old && ".mv." %s %s",
  736. \ new, s:plug_source, mee, mee, new, mee))
  737. if v:shell_error == 0
  738. unlet g:loaded_plug
  739. echo "Downloaded ". s:plug_source
  740. return 1
  741. else
  742. echoerr "Error upgrading vim-plug"
  743. return 0
  744. endif
  745. elseif has('ruby')
  746. echo "Downloading ". s:plug_source
  747. ruby << EOF
  748. require 'open-uri'
  749. require 'fileutils'
  750. me = VIM::evaluate('s:me')
  751. old = me + '.old'
  752. new = me + '.new'
  753. File.open(new, 'w') do |f|
  754. f << open(VIM::evaluate('s:plug_source')).read
  755. end
  756. FileUtils.cp me, old
  757. File.rename new, me
  758. EOF
  759. unlet g:loaded_plug
  760. echo "Downloaded ". s:plug_source
  761. return 1
  762. else
  763. echoerr "curl executable or ruby support not found"
  764. return 0
  765. endif
  766. endfunction
  767. function! s:status()
  768. call s:prepare()
  769. call append(0, 'Checking plugins')
  770. call append(1, '')
  771. let ecnt = 0
  772. let [cnt, total] = [0, len(g:plugs)]
  773. for [name, spec] in items(g:plugs)
  774. if isdirectory(spec.dir)
  775. let [valid, msg] = s:git_valid(spec, 1, 1)
  776. else
  777. let [valid, msg] = [0, 'Not found. Try PlugInstall.']
  778. endif
  779. let cnt += 1
  780. let ecnt += !valid
  781. call s:progress_bar(2, repeat('=', cnt), total)
  782. call append(3, s:format_message(valid, name, msg))
  783. normal! 2G
  784. redraw
  785. endfor
  786. call setline(1, 'Finished. '.ecnt.' error(s).')
  787. normal! gg
  788. endfunction
  789. function! s:is_preview_window_open()
  790. silent! wincmd P
  791. if &previewwindow
  792. wincmd p
  793. return 1
  794. endif
  795. return 0
  796. endfunction
  797. function! s:preview_commit()
  798. if b:plug_preview < 0
  799. let b:plug_preview = !s:is_preview_window_open()
  800. endif
  801. let sha = matchstr(getline('.'), '\(^ \)\@<=[0-9a-z]\{7}')
  802. if !empty(sha)
  803. let lnum = line('.')
  804. while lnum > 1
  805. let lnum -= 1
  806. let line = getline(lnum)
  807. let name = matchstr(line, '\(^- \)\@<=[^:]\+')
  808. if !empty(name)
  809. let dir = g:plugs[name].dir
  810. if isdirectory(dir)
  811. execute 'cd '.s:esc(dir)
  812. execute 'pedit '.sha
  813. wincmd P
  814. setlocal filetype=git buftype=nofile nobuflisted
  815. execute 'silent read !git show '.sha
  816. normal! ggdd
  817. wincmd p
  818. cd -
  819. endif
  820. break
  821. endif
  822. endwhile
  823. endif
  824. endfunction
  825. function! s:section(flags)
  826. call search('\(^- \)\@<=.', a:flags)
  827. endfunction
  828. function! s:diff()
  829. call s:prepare()
  830. call append(0, 'Collecting updated changes ...')
  831. normal! gg
  832. redraw
  833. let cnt = 0
  834. for [k, v] in items(g:plugs)
  835. if !isdirectory(v.dir)
  836. continue
  837. endif
  838. execute 'cd '.s:esc(v.dir)
  839. let diff = system('git log --pretty=format:"%h %s (%cr)" "HEAD@{0}...HEAD@{1}"')
  840. if !v:shell_error && !empty(diff)
  841. call append(1, '')
  842. call append(2, '- '.k.':')
  843. call append(3, map(split(diff, '\n'), '" ". v:val'))
  844. let cnt += 1
  845. normal! gg
  846. redraw
  847. endif
  848. cd -
  849. endfor
  850. call setline(1, cnt == 0 ? 'No updates.' : 'Last update:')
  851. nnoremap <silent> <buffer> <cr> :silent! call <SID>preview_commit()<cr>
  852. normal! gg
  853. endfunction
  854. let &cpo = s:cpo_save
  855. unlet s:cpo_save