plug.vim 14 KB

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