plug.vim 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  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. refill = proc { |name|
  317. mtx.synchronize do
  318. all.merge! VIM::evaluate("s:extend(['#{name}'])")
  319. logh.call
  320. end
  321. }
  322. VIM::evaluate('a:threads').to_i.times.map { |i|
  323. Thread.new(i) do |ii|
  324. while pair = take1.call
  325. name = pair.first
  326. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  327. ok, result =
  328. if File.directory? dir
  329. current_uri = `#{cd} #{dir} && git config remote.origin.url`.chomp
  330. if $? == 0 && current_uri == uri
  331. if pull
  332. [true, `#{cd} #{dir} && git checkout -q #{branch} && git pull origin #{branch} 2>&1`]
  333. else
  334. [true, skip]
  335. end
  336. else
  337. [false, "PlugClean required. Invalid remote."]
  338. end
  339. else
  340. FileUtils.mkdir_p(base)
  341. r = `#{cd} #{base} && git clone --recursive #{uri} -b #{branch} #{dir} 2>&1`
  342. [$? == 0, r]
  343. end
  344. result = result.lines.to_a.last.strip
  345. log.call name, result, ok
  346. refill.call name
  347. end
  348. end
  349. }.each(&:join)
  350. $curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
  351. EOF
  352. endfunction
  353. function! s:path(path)
  354. return substitute(s:is_win ? substitute(a:path, '/', '\', 'g') : a:path,
  355. \ '[/\\]*$', '', '')
  356. endfunction
  357. function! s:glob_dir(path)
  358. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:path(v:val)')
  359. endfunction
  360. function! s:progress_bar(line, cnt, total)
  361. call setline(a:line, '[' . s:lpad(repeat('=', a:cnt), a:total) . ']')
  362. endfunction
  363. function! s:git_valid(spec, cd)
  364. if isdirectory(a:spec.dir)
  365. if a:cd | execute "cd " . a:spec.dir | endif
  366. let ret = s:system("git config remote.origin.url") == a:spec.uri
  367. if a:cd | cd - | endif
  368. else
  369. let ret = 0
  370. endif
  371. return ret
  372. endfunction
  373. function! s:clean(force)
  374. call s:prepare()
  375. call append(0, 'Searching for unused plugins in '.g:plug_home)
  376. call append(1, '')
  377. " List of valid directories
  378. let dirs = []
  379. let [cnt, total] = [0, len(g:plugs)]
  380. for spec in values(g:plugs)
  381. if s:git_valid(spec, 1)
  382. call add(dirs, spec.dir)
  383. endif
  384. let cnt += 1
  385. call s:progress_bar(2, cnt, total)
  386. redraw
  387. endfor
  388. let alldirs = dirs +
  389. \ map(copy(dirs), 'fnamemodify(v:val, ":h")')
  390. for dir in dirs
  391. let alldirs += s:glob_dir(dir)
  392. endfor
  393. let allowed = {}
  394. for dir in alldirs
  395. let allowed[dir] = 1
  396. endfor
  397. let todo = []
  398. let found = sort(s:glob_dir(g:plug_home))
  399. while !empty(found)
  400. let f = remove(found, 0)
  401. if !has_key(allowed, f) && isdirectory(f)
  402. call add(todo, f)
  403. call append(line('$'), '- ' . f)
  404. let found = filter(found, 'stridx(v:val, f) != 0')
  405. end
  406. endwhile
  407. normal! G
  408. redraw
  409. if empty(todo)
  410. call append(line('$'), 'Already clean.')
  411. else
  412. call inputsave()
  413. let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
  414. call inputrestore()
  415. if yes
  416. for dir in todo
  417. if isdirectory(dir)
  418. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . dir)
  419. endif
  420. endfor
  421. call append(line('$'), 'Removed.')
  422. else
  423. call append(line('$'), 'Cancelled.')
  424. endif
  425. endif
  426. normal! G
  427. endfunction
  428. function! s:upgrade()
  429. if executable('curl')
  430. let mee = shellescape(s:me)
  431. let new = shellescape(s:me . '.new')
  432. echo "Downloading ". s:plug_source
  433. redraw
  434. let mv = s:is_win ? 'move /Y' : 'mv -f'
  435. call system(printf(
  436. \ "curl -fLo %s %s && ".mv." %s %s.old && ".mv." %s %s",
  437. \ new, s:plug_source, mee, mee, new, mee))
  438. if v:shell_error == 0
  439. unlet g:loaded_plug
  440. echo "Downloaded ". s:plug_source
  441. return 1
  442. else
  443. echoerr "Error upgrading vim-plug"
  444. return 0
  445. endif
  446. else
  447. echoerr "`curl' not found"
  448. return 0
  449. endif
  450. endfunction
  451. function! s:status()
  452. call s:prepare()
  453. call append(0, 'Checking plugins')
  454. let errs = 0
  455. for [name, spec] in items(g:plugs)
  456. let err = 'OK'
  457. if isdirectory(spec.dir)
  458. execute 'cd '.spec.dir
  459. if s:git_valid(spec, 0)
  460. let branch = s:system('git rev-parse --abbrev-ref HEAD')
  461. if spec.branch != branch
  462. let err = '(x) Invalid branch: '.branch.'. Try PlugUpdate.'
  463. endif
  464. else
  465. let err = '(x) Invalid remote. Try PlugClean.'
  466. endif
  467. cd -
  468. else
  469. let err = '(x) Not found. Try PlugInstall.'
  470. endif
  471. let errs += err != 'OK'
  472. call append(2, printf('- %s: %s', name, err))
  473. call cursor(3, 1)
  474. redraw
  475. endfor
  476. call setline(1, 'Finished. '.errs.' error(s).')
  477. endfunction