plug.vim 14 KB

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