plug.vim 23 KB

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