plug.vim 24 KB

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