plug.vim 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  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'
  15. " Plug 'junegunn/vim-easy-align'
  16. " " Plug 'user/repo1', 'branch_or_tag'
  17. " " Plug 'user/repo2', { 'rtp': 'vim/plugin/dir', 'branch': 'devel' }
  18. " " ...
  19. "
  20. " call plug#end()
  21. "
  22. " Then :PlugInstall to install plugins. (default: ~/.vim/plugged)
  23. " You can change the location of the plugins with plug#begin(path) call.
  24. "
  25. "
  26. " Copyright (c) 2013 Junegunn Choi
  27. "
  28. " MIT License
  29. "
  30. " Permission is hereby granted, free of charge, to any person obtaining
  31. " a copy of this software and associated documentation files (the
  32. " "Software"), to deal in the Software without restriction, including
  33. " without limitation the rights to use, copy, modify, merge, publish,
  34. " distribute, sublicense, and/or sell copies of the Software, and to
  35. " permit persons to whom the Software is furnished to do so, subject to
  36. " the following conditions:
  37. "
  38. " The above copyright notice and this permission notice shall be
  39. " included in all copies or substantial portions of the Software.
  40. "
  41. " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  42. " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  43. " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  44. " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  45. " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  46. " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  47. " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  48. if exists('g:loaded_plug')
  49. finish
  50. endif
  51. let g:loaded_plug = 1
  52. let s:plug_source = 'https://raw.github.com/junegunn/vim-plug/master/plug.vim'
  53. let s:plug_file = 'Plugfile'
  54. let s:plug_win = 0
  55. let s:is_win = has('win32') || has('win64')
  56. let s:me = expand('<sfile>:p')
  57. function! plug#begin(...)
  58. let home = a:0 > 0 ? fnamemodify(a:1, ':p') :
  59. \ get(g:, 'plug_home', split(&rtp, ',')[0].'/plugged')
  60. if !isdirectory(home)
  61. try
  62. call mkdir(home, 'p')
  63. catch
  64. echoerr 'Invalid plug directory: '. home
  65. return 0
  66. endtry
  67. endif
  68. if !executable('git')
  69. echoerr "`git' executable not found. vim-plug requires git."
  70. return 0
  71. endif
  72. let g:plug_home = home
  73. let g:plugs = {}
  74. command! -nargs=+ Plug call s:add(1, <args>)
  75. command! -nargs=* PlugInstall call s:install(<f-args>)
  76. command! -nargs=* PlugUpdate call s:update(<f-args>)
  77. command! -nargs=0 -bang PlugClean call s:clean('<bang>' == '!')
  78. command! -nargs=0 PlugUpgrade if s:upgrade() | execute "source ". s:me | endif
  79. command! -nargs=0 PlugStatus call s:status()
  80. return 1
  81. endfunction
  82. function! plug#end()
  83. let keys = keys(g:plugs)
  84. while !empty(keys)
  85. let keys = keys(s:extend(keys))
  86. endwhile
  87. set nocompatible
  88. filetype off
  89. for plug in values(g:plugs)
  90. let rtp = s:rtp(plug)
  91. execute "set rtp^=".rtp
  92. if isdirectory(rtp.'after')
  93. execute "set rtp+=".rtp.'after'
  94. endif
  95. endfor
  96. filetype plugin indent on
  97. syntax on
  98. endfunction
  99. function! s:rtp(spec)
  100. return s:dirpath(a:spec.dir . get(a:spec, 'rtp', ''))
  101. endfunction
  102. function! s:add(...)
  103. let force = a:1
  104. let opts = { 'branch': 'master' }
  105. if a:0 == 2
  106. let plugin = a:2
  107. elseif a:0 == 3
  108. let plugin = a:2
  109. if type(a:3) == 1
  110. let opts.branch = a:3
  111. elseif type(a:3) == 4
  112. call extend(opts, a:3)
  113. else
  114. echoerr "Invalid argument type (expected: string or dictionary)"
  115. return
  116. endif
  117. else
  118. echoerr "Invalid number of arguments (1..2)"
  119. return
  120. endif
  121. if plugin =~ ':'
  122. let uri = plugin
  123. else
  124. if plugin !~ '/'
  125. let plugin = 'vim-scripts/'. plugin
  126. endif
  127. let uri = 'https://git:@github.com/' . plugin . '.git'
  128. endif
  129. let name = substitute(split(plugin, '/')[-1], '\.git$', '', '')
  130. if !force && has_key(g:plugs, name) | return | endif
  131. let dir = s:dirpath( fnamemodify(join([g:plug_home, name], '/'), ':p') )
  132. let spec = extend(opts, { 'dir': dir, 'uri': uri })
  133. let g:plugs[name] = spec
  134. endfunction
  135. function! s:install(...)
  136. call s:update_impl(0, a:000)
  137. endfunction
  138. function! s:update(...)
  139. call s:update_impl(1, a:000)
  140. endfunction
  141. function! s:apply()
  142. for spec in values(g:plugs)
  143. let docd = join([spec.dir, 'doc'], '/')
  144. if isdirectory(docd)
  145. execute "helptags ". join([spec.dir, 'doc'], '/')
  146. endif
  147. endfor
  148. runtime! plugin/*.vim
  149. runtime! after/*.vim
  150. silent! source $MYVIMRC
  151. endfunction
  152. function! s:syntax()
  153. syntax clear
  154. syntax region plug1 start=/\%1l/ end=/\%2l/ contains=ALL
  155. syntax region plug2 start=/\%2l/ end=/\%3l/ contains=ALL
  156. syn match plugNumber /[0-9]\+[0-9.]*/ containedin=plug1 contained
  157. syn match plugBracket /[[\]]/ containedin=plug2 contained
  158. syn match plugDash /^-/
  159. syn match plugName /\(^- \)\@<=[^:]*/
  160. syn match plugError /^- [^:]\+: (x).*/
  161. hi def link plug1 Title
  162. hi def link plug2 Repeat
  163. hi def link plugBracket Structure
  164. hi def link plugNumber Number
  165. hi def link plugDash Special
  166. hi def link plugName Label
  167. hi def link plugError Error
  168. endfunction
  169. function! s:lpad(str, len)
  170. return a:str . repeat(' ', a:len - len(a:str))
  171. endfunction
  172. function! s:system(cmd)
  173. let lines = split(system(a:cmd), '\n')
  174. return get(lines, -1, '')
  175. endfunction
  176. function! s:prepare()
  177. execute s:plug_win . 'wincmd w'
  178. if exists('b:plug')
  179. %d
  180. else
  181. vertical topleft new
  182. noremap <silent> <buffer> q :q<cr>
  183. let b:plug = 1
  184. let s:plug_win = winnr()
  185. call s:assign_name()
  186. endif
  187. setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline
  188. setf vim-plug
  189. call s:syntax()
  190. endfunction
  191. function! s:assign_name()
  192. " Assign buffer name
  193. let prefix = '[Plugins]'
  194. let name = prefix
  195. let idx = 2
  196. while bufexists(name)
  197. let name = printf("%s (%s)", prefix, idx)
  198. let idx = idx + 1
  199. endwhile
  200. silent! execute "f ".fnameescape(name)
  201. endfunction
  202. function! s:finish()
  203. call append(line('$'), '')
  204. call append(line('$'), 'Finishing ... ')
  205. redraw
  206. call s:apply()
  207. call s:syntax()
  208. call setline(line('$'), getline(line('$')) . 'Done!')
  209. normal! G
  210. endfunction
  211. function! s:update_impl(pull, args)
  212. let threads = len(a:args) > 0 ? a:args[0] : get(g:, 'plug_threads', 16)
  213. call s:prepare()
  214. call append(0, a:pull ? 'Updating plugins' : 'Installing plugins')
  215. call append(1, '['. s:lpad('', len(g:plugs)) .']')
  216. normal! 2G
  217. redraw
  218. if has('ruby') && threads > 1
  219. call s:update_parallel(a:pull, threads)
  220. else
  221. call s:update_serial(a:pull)
  222. endif
  223. call s:finish()
  224. endfunction
  225. function! s:extend(names)
  226. let prev = copy(g:plugs)
  227. try
  228. command! -nargs=+ Plug call s:add(0, <args>)
  229. for name in a:names
  230. let plugfile = s:rtp(g:plugs[name]) . s:plug_file
  231. if filereadable(plugfile)
  232. execute "source ". plugfile
  233. endif
  234. endfor
  235. finally
  236. command! -nargs=+ Plug call s:add(1, <args>)
  237. endtry
  238. return filter(copy(g:plugs), '!has_key(prev, v:key)')
  239. endfunction
  240. function! s:update_progress(pull, cnt, total)
  241. call setline(1, (a:pull ? 'Updating' : 'Installing').
  242. \ " plugins (".a:cnt."/".a:total.")")
  243. call s:progress_bar(2, a:cnt, a:total)
  244. normal! 2G
  245. redraw
  246. endfunction
  247. function! s:update_serial(pull)
  248. let st = reltime()
  249. let base = g:plug_home
  250. let todo = copy(g:plugs)
  251. let total = len(todo)
  252. let done = {}
  253. while !empty(todo)
  254. for [name, spec] in items(todo)
  255. let done[name] = 1
  256. if isdirectory(spec.dir)
  257. execute 'cd '.spec.dir
  258. let [valid, msg] = s:git_valid(spec, 0)
  259. if valid
  260. let result = a:pull ?
  261. \ s:system(
  262. \ printf('git checkout -q %s 2>&1 && git pull origin %s 2>&1',
  263. \ spec.branch, spec.branch)) : 'Already installed'
  264. let error = a:pull ? v:shell_error != 0 : 0
  265. else
  266. let result = msg
  267. let error = 1
  268. endif
  269. else
  270. if !isdirectory(base)
  271. call mkdir(base, 'p')
  272. endif
  273. execute 'cd '.base
  274. let d = shellescape(substitute(spec.dir, '[\/]\+$', '', ''))
  275. let result = s:system(
  276. \ printf('git clone --recursive %s -b %s %s 2>&1',
  277. \ shellescape(spec.uri), shellescape(spec.branch), d))
  278. let error = v:shell_error != 0
  279. endif
  280. cd -
  281. if error
  282. let result = '(x) ' . result
  283. endif
  284. call append(3, '- ' . name . ': ' . result)
  285. call s:update_progress(a:pull, len(done), total)
  286. endfor
  287. if !empty(s:extend(keys(todo)))
  288. let todo = filter(copy(g:plugs), '!has_key(done, v:key)')
  289. let total += len(todo)
  290. call s:update_progress(a:pull, len(done), total)
  291. else
  292. break
  293. endif
  294. endwhile
  295. call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
  296. endfunction
  297. function! s:update_parallel(pull, threads)
  298. ruby << EOF
  299. require 'thread'
  300. require 'fileutils'
  301. require 'timeout'
  302. st = Time.now
  303. iswin = VIM::evaluate('s:is_win').to_i == 1
  304. cd = iswin ? 'cd /d' : 'cd'
  305. pull = VIM::evaluate('a:pull').to_i == 1
  306. base = VIM::evaluate('g:plug_home')
  307. all = VIM::evaluate('copy(g:plugs)')
  308. limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
  309. done = {}
  310. skip = 'Already installed'
  311. mtx = Mutex.new
  312. take1 = proc { mtx.synchronize { all.shift } }
  313. logh = proc {
  314. cnt, tot = done.length, VIM::evaluate('len(g:plugs)')
  315. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  316. $curbuf[2] = '[' + ('=' * cnt).ljust(tot) + ']'
  317. VIM::command('normal! 2G')
  318. VIM::command('redraw') unless iswin
  319. }
  320. log = proc { |name, result, ok|
  321. mtx.synchronize do
  322. done[name] = true
  323. result = '(x) ' + result unless ok
  324. result = "- #{name}: #{result}"
  325. $curbuf.append 3, result
  326. logh.call
  327. end
  328. }
  329. bt = proc { |cmd|
  330. begin
  331. Timeout::timeout(limit) do
  332. if iswin
  333. tmp = VIM::evaluate('tempname()')
  334. system("#{cmd} > #{tmp}")
  335. data = File.read(tmp).chomp
  336. File.unlink tmp rescue nil
  337. else
  338. data = `#{cmd}`.chomp
  339. end
  340. [$? == 0, data]
  341. end
  342. rescue Timeout::Error
  343. [false, "Timeout!"]
  344. end
  345. }
  346. until all.empty?
  347. names = all.keys
  348. [names.length, VIM::evaluate('a:threads').to_i].min.times.map { |i|
  349. Thread.new(i) do
  350. while pair = take1.call
  351. name = pair.first
  352. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  353. ok, result =
  354. if File.directory? dir
  355. ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url"
  356. current_uri = data.lines.to_a.last
  357. if ret && current_uri == uri
  358. if pull
  359. bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && git pull origin #{branch} 2>&1"
  360. else
  361. [true, skip]
  362. end
  363. else
  364. [false, "PlugClean required. Invalid status."]
  365. end
  366. else
  367. FileUtils.mkdir_p(base)
  368. d = dir.sub(%r{[\\/]+$}, '')
  369. bt.call "#{cd} #{base} && git clone --recursive #{uri} -b #{branch} #{d} 2>&1"
  370. end
  371. result = result.lines.to_a.last
  372. log.call name, (result && result.strip), ok
  373. end
  374. end
  375. }.each(&:join)
  376. all.merge! VIM::evaluate("s:extend(#{names.inspect})")
  377. logh.call
  378. end
  379. $curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
  380. EOF
  381. endfunction
  382. function! s:path(path)
  383. return substitute(s:is_win ? substitute(a:path, '/', '\', 'g') : a:path,
  384. \ '[/\\]*$', '', '')
  385. endfunction
  386. function! s:dirpath(path)
  387. let path = s:path(a:path)
  388. if s:is_win
  389. return path !~ '\\$' ? path.'\' : path
  390. else
  391. return path !~ '/$' ? path.'/' : path
  392. endif
  393. endfunction
  394. function! s:glob_dir(path)
  395. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
  396. endfunction
  397. function! s:progress_bar(line, cnt, total)
  398. call setline(a:line, '[' . s:lpad(repeat('=', a:cnt), a:total) . ']')
  399. endfunction
  400. function! s:git_valid(spec, cd)
  401. let ret = 1
  402. let msg = 'OK'
  403. if isdirectory(a:spec.dir)
  404. if a:cd | execute "cd " . a:spec.dir | endif
  405. let remote = s:system("git config remote.origin.url")
  406. if remote != a:spec.uri
  407. let msg = 'Invalid remote: ' . remote . '. Try PlugClean.'
  408. let ret = 0
  409. else
  410. let branch = s:system('git rev-parse --abbrev-ref HEAD')
  411. if v:shell_error != 0
  412. let msg = 'Invalid git repository. Try PlugClean.'
  413. let ret = 0
  414. elseif a:spec.branch != branch
  415. let msg = 'Invalid branch: '.branch.'. Try PlugUpdate.'
  416. let ret = 0
  417. endif
  418. endif
  419. if a:cd | cd - | endif
  420. else
  421. let msg = 'Not found'
  422. let ret = 0
  423. endif
  424. return [ret, msg]
  425. endfunction
  426. function! s:clean(force)
  427. call s:prepare()
  428. call append(0, 'Searching for unused plugins in '.g:plug_home)
  429. call append(1, '')
  430. " List of valid directories
  431. let dirs = []
  432. let [cnt, total] = [0, len(g:plugs)]
  433. for spec in values(g:plugs)
  434. if s:git_valid(spec, 1)[0]
  435. call add(dirs, spec.dir)
  436. endif
  437. let cnt += 1
  438. call s:progress_bar(2, cnt, total)
  439. redraw
  440. endfor
  441. let allowed = {}
  442. for dir in dirs
  443. let allowed[dir] = 1
  444. for child in s:glob_dir(dir)
  445. let allowed[child] = 1
  446. endfor
  447. endfor
  448. let todo = []
  449. let found = sort(s:glob_dir(g:plug_home))
  450. while !empty(found)
  451. let f = remove(found, 0)
  452. if !has_key(allowed, f) && isdirectory(f)
  453. call add(todo, f)
  454. call append(line('$'), '- ' . f)
  455. let found = filter(found, 'stridx(v:val, f) != 0')
  456. end
  457. endwhile
  458. normal! G
  459. redraw
  460. if empty(todo)
  461. call append(line('$'), 'Already clean.')
  462. else
  463. call inputsave()
  464. let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
  465. call inputrestore()
  466. if yes
  467. for dir in todo
  468. if isdirectory(dir)
  469. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . dir)
  470. endif
  471. endfor
  472. call append(line('$'), 'Removed.')
  473. else
  474. call append(line('$'), 'Cancelled.')
  475. endif
  476. endif
  477. normal! G
  478. endfunction
  479. function! s:upgrade()
  480. if executable('curl')
  481. let mee = shellescape(s:me)
  482. let new = shellescape(s:me . '.new')
  483. echo "Downloading ". s:plug_source
  484. redraw
  485. let mv = s:is_win ? 'move /Y' : 'mv -f'
  486. call system(printf(
  487. \ "curl -fLo %s %s && ".mv." %s %s.old && ".mv." %s %s",
  488. \ new, s:plug_source, mee, mee, new, mee))
  489. if v:shell_error == 0
  490. unlet g:loaded_plug
  491. echo "Downloaded ". s:plug_source
  492. return 1
  493. else
  494. echoerr "Error upgrading vim-plug"
  495. return 0
  496. endif
  497. else
  498. echoerr "`curl' not found"
  499. return 0
  500. endif
  501. endfunction
  502. function! s:status()
  503. call s:prepare()
  504. call append(0, 'Checking plugins')
  505. let errs = 0
  506. for [name, spec] in items(g:plugs)
  507. let err = 'OK'
  508. if isdirectory(spec.dir)
  509. execute 'cd '.spec.dir
  510. let [valid, msg] = s:git_valid(spec, 0)
  511. if !valid
  512. let err = '(x) '. msg
  513. endif
  514. cd -
  515. else
  516. let err = '(x) Not found. Try PlugInstall.'
  517. endif
  518. let errs += err != 'OK'
  519. call append(2, printf('- %s: %s', name, err))
  520. call cursor(3, 1)
  521. redraw
  522. endfor
  523. call setline(1, 'Finished. '.errs.' error(s).')
  524. endfunction