plug.vim 24 KB

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