plug.vim 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  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
  66. endtry
  67. endif
  68. if !executable('git')
  69. echoerr "`git' executable not found. vim-plug requires git."
  70. return
  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. endfunction
  81. function! plug#end()
  82. let keys = keys(g:plugs)
  83. while !empty(keys)
  84. let keys = keys(s:extend(keys))
  85. endwhile
  86. set nocompatible
  87. filetype off
  88. for plug in values(g:plugs)
  89. let rtp = s:rtp(plug)
  90. execute "set rtp^=".rtp
  91. if isdirectory(rtp.'after')
  92. execute "set rtp+=".rtp.'after'
  93. endif
  94. endfor
  95. filetype plugin indent on
  96. syntax on
  97. endfunction
  98. function! s:rtp(spec)
  99. let rtp = a:spec.dir . get(a:spec, 'rtp', '')
  100. if rtp !~ '/$' | let rtp .= '/' | endif
  101. return rtp
  102. endfunction
  103. function! s:add(...)
  104. let force = a:1
  105. let opts = { 'branch': 'master' }
  106. if a:0 == 2
  107. let plugin = a:2
  108. elseif a:0 == 3
  109. let plugin = a:2
  110. if type(a:3) == 1
  111. let opts.branch = a:3
  112. elseif type(a:3) == 4
  113. call extend(opts, a:3)
  114. else
  115. echoerr "Invalid argument type (expected: string or dictionary)"
  116. return
  117. endif
  118. else
  119. echoerr "Invalid number of arguments (1..2)"
  120. return
  121. endif
  122. if plugin =~ ':'
  123. let uri = plugin
  124. else
  125. if plugin !~ '/'
  126. let plugin = 'vim-scripts/'. plugin
  127. endif
  128. let uri = 'https://git:@github.com/' . plugin . '.git'
  129. endif
  130. let name = substitute(split(plugin, '/')[-1], '\.git$', '', '')
  131. if !force && has_key(g:plugs, name) | return | endif
  132. let dir = fnamemodify(join([g:plug_home, name], '/'), ':p')
  133. if dir !~ '/$' | let dir .= '/' | endif
  134. let spec = extend(opts, { 'dir': dir, 'uri': uri })
  135. let g:plugs[name] = spec
  136. endfunction
  137. function! s:install(...)
  138. call s:update_impl(0, a:000)
  139. endfunction
  140. function! s:update(...)
  141. call s:update_impl(1, a:000)
  142. endfunction
  143. function! s:apply()
  144. for spec in values(g:plugs)
  145. let docd = join([spec.dir, 'doc'], '/')
  146. if isdirectory(docd)
  147. execute "helptags ". join([spec.dir, 'doc'], '/')
  148. endif
  149. endfor
  150. runtime! plugin/*.vim
  151. runtime! after/*.vim
  152. silent! source $MYVIMRC
  153. endfunction
  154. function! s:syntax()
  155. syntax clear
  156. syntax region plug1 start=/\%1l/ end=/\%2l/ contains=ALL
  157. syntax region plug2 start=/\%2l/ end=/\%3l/ contains=ALL
  158. syn match plugNumber /[0-9]\+[0-9.]*/ containedin=plug1 contained
  159. syn match plugBracket /[[\]]/ containedin=plug2 contained
  160. syn match plugDash /^-/
  161. syn match plugName /\(^- \)\@<=[^:]*/
  162. syn match plugError /^- [^:]\+: (x).*/
  163. hi def link plug1 Title
  164. hi def link plug2 Repeat
  165. hi def link plugBracket Structure
  166. hi def link plugNumber Number
  167. hi def link plugDash Special
  168. hi def link plugName Label
  169. hi def link plugError Error
  170. endfunction
  171. function! s:lpad(str, len)
  172. return a:str . repeat(' ', a:len - len(a:str))
  173. endfunction
  174. function! s:system(cmd)
  175. let lines = split(system(a:cmd), '\n')
  176. return get(lines, -1, '')
  177. endfunction
  178. function! s:prepare()
  179. execute s:plug_win . 'wincmd w'
  180. if exists('b:plug')
  181. %d
  182. else
  183. vertical topleft new
  184. noremap <silent> <buffer> q :q<cr>
  185. let b:plug = 1
  186. let s:plug_win = winnr()
  187. call s:assign_name()
  188. endif
  189. setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline
  190. setf vim-plug
  191. call s:syntax()
  192. endfunction
  193. function! s:assign_name()
  194. " Assign buffer name
  195. let prefix = '[Plugins]'
  196. let name = prefix
  197. let idx = 2
  198. while bufexists(name)
  199. let name = printf("%s (%s)", prefix, idx)
  200. let idx = idx + 1
  201. endwhile
  202. silent! execute "f ".fnameescape(name)
  203. endfunction
  204. function! s:finish()
  205. call append(line('$'), '')
  206. call append(line('$'), 'Finishing ... ')
  207. redraw
  208. call s:apply()
  209. call s:syntax()
  210. call setline(line('$'), getline(line('$')) . 'Done!')
  211. normal! G
  212. endfunction
  213. function! s:update_impl(pull, args)
  214. let threads = len(a:args) > 0 ? a:args[0] : get(g:, 'plug_threads', 16)
  215. call s:prepare()
  216. call append(0, a:pull ? 'Updating plugins' : 'Installing plugins')
  217. call append(1, '['. s:lpad('', len(g:plugs)) .']')
  218. normal! 2G
  219. redraw
  220. if has('ruby') && threads > 1
  221. call s:update_parallel(a:pull, threads)
  222. else
  223. call s:update_serial(a:pull)
  224. endif
  225. call s:finish()
  226. endfunction
  227. function! s:extend(names)
  228. let prev = copy(g:plugs)
  229. try
  230. command! -nargs=+ Plug call s:add(0, <args>)
  231. for name in a:names
  232. let plugfile = s:rtp(g:plugs[name]) . s:plug_file
  233. if filereadable(plugfile)
  234. execute "source ". plugfile
  235. endif
  236. endfor
  237. finally
  238. command! -nargs=+ Plug call s:add(1, <args>)
  239. endtry
  240. return filter(copy(g:plugs), '!has_key(prev, v:key)')
  241. endfunction
  242. function! s:update_progress(pull, cnt, total)
  243. call setline(1, (a:pull ? 'Updating' : 'Installing').
  244. \ " plugins (".a:cnt."/".a:total.")")
  245. call s:progress_bar(2, a:cnt, a:total)
  246. normal! 2G
  247. redraw
  248. endfunction
  249. function! s:update_serial(pull)
  250. let st = reltime()
  251. let base = g:plug_home
  252. let todo = copy(g:plugs)
  253. let total = len(todo)
  254. let done = {}
  255. while !empty(todo)
  256. for [name, spec] in items(todo)
  257. let done[name] = 1
  258. let d = shellescape(spec.dir)
  259. if isdirectory(spec.dir)
  260. execute 'cd '.spec.dir
  261. if s:git_valid(spec, 0)
  262. let result = a:pull ?
  263. \ s:system(
  264. \ printf('git checkout -q %s 2>&1 && git pull origin %s 2>&1',
  265. \ spec.branch, spec.branch)) : 'Already installed'
  266. let error = a:pull ? v:shell_error != 0 : 0
  267. else
  268. let result = "PlugClean required. Invalid remote."
  269. let error = 1
  270. endif
  271. else
  272. if !isdirectory(base)
  273. call mkdir(base, 'p')
  274. endif
  275. execute 'cd '.base
  276. let result = s:system(
  277. \ printf('git clone --recursive %s -b %s %s 2>&1',
  278. \ shellescape(spec.uri), shellescape(spec.branch), d))
  279. let error = v:shell_error != 0
  280. endif
  281. cd -
  282. if error
  283. let result = '(x) ' . result
  284. endif
  285. call append(3, '- ' . name . ': ' . result)
  286. call s:update_progress(a:pull, len(done), total)
  287. endfor
  288. if !empty(s:extend(keys(todo)))
  289. let todo = filter(copy(g:plugs), '!has_key(done, v:key)')
  290. let total += len(todo)
  291. call s:update_progress(a:pull, len(done), total)
  292. else
  293. break
  294. endif
  295. endwhile
  296. call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
  297. endfunction
  298. function! s:update_parallel(pull, threads)
  299. ruby << EOF
  300. require 'thread'
  301. require 'fileutils'
  302. st = Time.now
  303. cd = VIM::evaluate('s:is_win').to_i == 1 ? 'cd /d' : 'cd'
  304. pull = VIM::evaluate('a:pull').to_i == 1
  305. base = VIM::evaluate('g:plug_home')
  306. all = VIM::evaluate('copy(g:plugs)')
  307. done = {}
  308. skip = 'Already installed'
  309. mtx = Mutex.new
  310. take1 = proc { mtx.synchronize { all.shift } }
  311. logh = proc {
  312. cnt, tot = done.length, VIM::evaluate('len(g:plugs)')
  313. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  314. $curbuf[2] = '[' + ('=' * cnt).ljust(tot) + ']'
  315. VIM::command('normal! 2G')
  316. VIM::command('redraw')
  317. }
  318. log = proc { |name, result, ok|
  319. mtx.synchronize do
  320. done[name] = true
  321. result = '(x) ' + result unless ok
  322. result = "- #{name}: #{result}"
  323. $curbuf.append 3, result
  324. logh.call
  325. end
  326. }
  327. until all.empty?
  328. names = all.keys
  329. [names.length, VIM::evaluate('a:threads').to_i].min.times.map { |i|
  330. Thread.new(i) do
  331. while pair = take1.call
  332. name = pair.first
  333. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  334. ok, result =
  335. if File.directory? dir
  336. current_uri = `#{cd} #{dir} && git config remote.origin.url`.chomp
  337. if $? == 0 && current_uri == uri
  338. if pull
  339. output = `#{cd} #{dir} && git checkout -q #{branch} 2>&1 && git pull origin #{branch} 2>&1`
  340. [$? == 0, output]
  341. else
  342. [true, skip]
  343. end
  344. else
  345. [false, "PlugClean required. Invalid remote."]
  346. end
  347. else
  348. FileUtils.mkdir_p(base)
  349. r = `#{cd} #{base} && git clone --recursive #{uri} -b #{branch} #{dir} 2>&1`
  350. [$? == 0, r]
  351. end
  352. result = result.lines.to_a.last.strip
  353. log.call name, result, ok
  354. end
  355. end
  356. }.each(&:join)
  357. all.merge! VIM::evaluate("s:extend(#{names.inspect})")
  358. logh.call
  359. end
  360. $curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
  361. EOF
  362. endfunction
  363. function! s:path(path)
  364. return substitute(s:is_win ? substitute(a:path, '/', '\', 'g') : a:path,
  365. \ '[/\\]*$', '', '')
  366. endfunction
  367. function! s:glob_dir(path)
  368. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:path(v:val)')
  369. endfunction
  370. function! s:progress_bar(line, cnt, total)
  371. call setline(a:line, '[' . s:lpad(repeat('=', a:cnt), a:total) . ']')
  372. endfunction
  373. function! s:git_valid(spec, cd)
  374. if isdirectory(a:spec.dir)
  375. if a:cd | execute "cd " . a:spec.dir | endif
  376. let ret = s:system("git config remote.origin.url") == a:spec.uri
  377. if a:cd | cd - | endif
  378. else
  379. let ret = 0
  380. endif
  381. return ret
  382. endfunction
  383. function! s:clean(force)
  384. call s:prepare()
  385. call append(0, 'Searching for unused plugins in '.g:plug_home)
  386. call append(1, '')
  387. " List of valid directories
  388. let dirs = []
  389. let [cnt, total] = [0, len(g:plugs)]
  390. for spec in values(g:plugs)
  391. if s:git_valid(spec, 1)
  392. call add(dirs, spec.dir)
  393. endif
  394. let cnt += 1
  395. call s:progress_bar(2, cnt, total)
  396. redraw
  397. endfor
  398. let alldirs = dirs +
  399. \ map(copy(dirs), 'fnamemodify(v:val, ":h")')
  400. for dir in dirs
  401. let alldirs += s:glob_dir(dir)
  402. endfor
  403. let allowed = {}
  404. for dir in alldirs
  405. let allowed[dir] = 1
  406. endfor
  407. let todo = []
  408. let found = sort(s:glob_dir(g:plug_home))
  409. while !empty(found)
  410. let f = remove(found, 0)
  411. if !has_key(allowed, f) && isdirectory(f)
  412. call add(todo, f)
  413. call append(line('$'), '- ' . f)
  414. let found = filter(found, 'stridx(v:val, f) != 0')
  415. end
  416. endwhile
  417. normal! G
  418. redraw
  419. if empty(todo)
  420. call append(line('$'), 'Already clean.')
  421. else
  422. call inputsave()
  423. let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
  424. call inputrestore()
  425. if yes
  426. for dir in todo
  427. if isdirectory(dir)
  428. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . dir)
  429. endif
  430. endfor
  431. call append(line('$'), 'Removed.')
  432. else
  433. call append(line('$'), 'Cancelled.')
  434. endif
  435. endif
  436. normal! G
  437. endfunction
  438. function! s:upgrade()
  439. if executable('curl')
  440. let mee = shellescape(s:me)
  441. let new = shellescape(s:me . '.new')
  442. echo "Downloading ". s:plug_source
  443. redraw
  444. let mv = s:is_win ? 'move /Y' : 'mv -f'
  445. call system(printf(
  446. \ "curl -fLo %s %s && ".mv." %s %s.old && ".mv." %s %s",
  447. \ new, s:plug_source, mee, mee, new, mee))
  448. if v:shell_error == 0
  449. unlet g:loaded_plug
  450. echo "Downloaded ". s:plug_source
  451. return 1
  452. else
  453. echoerr "Error upgrading vim-plug"
  454. return 0
  455. endif
  456. else
  457. echoerr "`curl' not found"
  458. return 0
  459. endif
  460. endfunction
  461. function! s:status()
  462. call s:prepare()
  463. call append(0, 'Checking plugins')
  464. let errs = 0
  465. for [name, spec] in items(g:plugs)
  466. let err = 'OK'
  467. if isdirectory(spec.dir)
  468. execute 'cd '.spec.dir
  469. if s:git_valid(spec, 0)
  470. let branch = s:system('git rev-parse --abbrev-ref HEAD')
  471. if spec.branch != branch
  472. let err = '(x) Invalid branch: '.branch.'. Try PlugUpdate.'
  473. endif
  474. else
  475. let err = '(x) Invalid remote. Try PlugClean.'
  476. endif
  477. cd -
  478. else
  479. let err = '(x) Not found. Try PlugInstall.'
  480. endif
  481. let errs += err != 'OK'
  482. call append(2, printf('- %s: %s', name, err))
  483. call cursor(3, 1)
  484. redraw
  485. endfor
  486. call setline(1, 'Finished. '.errs.' error(s).')
  487. endfunction