plug.vim 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  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:2, a:3]
  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. let threads = len(a:args) > 0 ? a:args[0] : get(g:, 'plug_threads', 16)
  199. call s:prepare()
  200. call append(0, a:pull ? 'Updating plugins' : 'Installing plugins')
  201. call append(1, '['. s:lpad('', len(g:plugs)) .']')
  202. normal! 2G
  203. redraw
  204. if has('ruby') && threads > 1
  205. call s:update_parallel(a:pull, threads)
  206. else
  207. call s:update_serial(a:pull)
  208. endif
  209. call s:finish()
  210. endfunction
  211. function! s:extend(names)
  212. let prev = copy(g:plugs)
  213. try
  214. command! -nargs=+ Plug call s:add(0, <args>)
  215. for name in a:names
  216. let spec = g:plugs[name]
  217. let plugfile = spec.dir . '/'. s:plug_file
  218. if filereadable(plugfile)
  219. execute "source ". plugfile
  220. endif
  221. endfor
  222. finally
  223. command! -nargs=+ Plug call s:add(1, <args>)
  224. endtry
  225. return filter(copy(g:plugs), '!has_key(prev, v:key)')
  226. endfunction
  227. function! s:update_progress(pull, cnt, total)
  228. call setline(1, (a:pull ? 'Updating' : 'Installing').
  229. \ " plugins (".a:cnt."/".a:total.")")
  230. call s:progress_bar(2, a:cnt, a:total)
  231. normal! 2G
  232. redraw
  233. endfunction
  234. function! s:update_serial(pull)
  235. let st = reltime()
  236. let base = g:plug_home
  237. let todo = copy(g:plugs)
  238. let total = len(todo)
  239. let done = {}
  240. while !empty(todo)
  241. for [name, spec] in items(todo)
  242. let done[name] = 1
  243. let d = shellescape(spec.dir)
  244. if isdirectory(spec.dir)
  245. execute 'cd '.spec.dir
  246. if s:git_valid(spec, 0)
  247. let result = a:pull ?
  248. \ s:system(
  249. \ printf('git checkout -q %s 2>&1 && git pull origin %s 2>&1',
  250. \ spec.branch, spec.branch)) : 'Already installed'
  251. let error = a:pull ? v:shell_error != 0 : 0
  252. else
  253. let result = "PlugClean required. Invalid remote."
  254. let error = 1
  255. endif
  256. else
  257. if !isdirectory(base)
  258. call mkdir(base, 'p')
  259. endif
  260. execute 'cd '.base
  261. let result = s:system(
  262. \ printf('git clone --recursive %s -b %s %s 2>&1',
  263. \ shellescape(spec.uri), shellescape(spec.branch), d))
  264. let error = v:shell_error != 0
  265. endif
  266. cd -
  267. if error
  268. let result = '(x) ' . result
  269. endif
  270. call append(3, '- ' . name . ': ' . result)
  271. call s:update_progress(a:pull, len(done), total)
  272. endfor
  273. if !empty(s:extend(keys(todo)))
  274. let todo = filter(copy(g:plugs), '!has_key(done, v:key)')
  275. let total += len(todo)
  276. call s:update_progress(a:pull, len(done), total)
  277. else
  278. break
  279. endif
  280. endwhile
  281. call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
  282. endfunction
  283. function! s:update_parallel(pull, threads)
  284. ruby << EOF
  285. require 'thread'
  286. require 'fileutils'
  287. st = Time.now
  288. cd = VIM::evaluate('s:is_win').to_i == 1 ? 'cd /d' : 'cd'
  289. pull = VIM::evaluate('a:pull').to_i == 1
  290. base = VIM::evaluate('g:plug_home')
  291. all = VIM::evaluate('copy(g:plugs)')
  292. done = {}
  293. skip = 'Already installed'
  294. mtx = Mutex.new
  295. take1 = proc { mtx.synchronize { all.shift } }
  296. logh = proc {
  297. cnt, tot = done.length, VIM::evaluate('len(g:plugs)')
  298. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  299. $curbuf[2] = '[' + ('=' * cnt).ljust(tot) + ']'
  300. VIM::command('normal! 2G')
  301. VIM::command('redraw')
  302. }
  303. log = proc { |name, result, ok|
  304. mtx.synchronize do
  305. done[name] = true
  306. result = '(x) ' + result unless ok
  307. result = "- #{name}: #{result}"
  308. $curbuf.append 3, result
  309. logh.call
  310. end
  311. }
  312. until all.empty?
  313. names = all.keys
  314. [names.length, VIM::evaluate('a:threads').to_i].min.times.map { |i|
  315. Thread.new(i) do
  316. while pair = take1.call
  317. name = pair.first
  318. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  319. ok, result =
  320. if File.directory? dir
  321. current_uri = `#{cd} #{dir} && git config remote.origin.url`.chomp
  322. if $? == 0 && current_uri == uri
  323. if pull
  324. output = `#{cd} #{dir} && git checkout -q #{branch} 2>&1 && git pull origin #{branch} 2>&1`
  325. [$? == 0, output]
  326. else
  327. [true, skip]
  328. end
  329. else
  330. [false, "PlugClean required. Invalid remote."]
  331. end
  332. else
  333. FileUtils.mkdir_p(base)
  334. r = `#{cd} #{base} && git clone --recursive #{uri} -b #{branch} #{dir} 2>&1`
  335. [$? == 0, r]
  336. end
  337. result = result.lines.to_a.last.strip
  338. log.call name, result, ok
  339. end
  340. end
  341. }.each(&:join)
  342. all.merge! VIM::evaluate("s:extend(#{names.inspect})")
  343. logh.call
  344. end
  345. $curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
  346. EOF
  347. endfunction
  348. function! s:path(path)
  349. return substitute(s:is_win ? substitute(a:path, '/', '\', 'g') : a:path,
  350. \ '[/\\]*$', '', '')
  351. endfunction
  352. function! s:glob_dir(path)
  353. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:path(v:val)')
  354. endfunction
  355. function! s:progress_bar(line, cnt, total)
  356. call setline(a:line, '[' . s:lpad(repeat('=', a:cnt), a:total) . ']')
  357. endfunction
  358. function! s:git_valid(spec, cd)
  359. if isdirectory(a:spec.dir)
  360. if a:cd | execute "cd " . a:spec.dir | endif
  361. let ret = s:system("git config remote.origin.url") == a:spec.uri
  362. if a:cd | cd - | endif
  363. else
  364. let ret = 0
  365. endif
  366. return ret
  367. endfunction
  368. function! s:clean(force)
  369. call s:prepare()
  370. call append(0, 'Searching for unused plugins in '.g:plug_home)
  371. call append(1, '')
  372. " List of valid directories
  373. let dirs = []
  374. let [cnt, total] = [0, len(g:plugs)]
  375. for spec in values(g:plugs)
  376. if s:git_valid(spec, 1)
  377. call add(dirs, spec.dir)
  378. endif
  379. let cnt += 1
  380. call s:progress_bar(2, cnt, total)
  381. redraw
  382. endfor
  383. let alldirs = dirs +
  384. \ map(copy(dirs), 'fnamemodify(v:val, ":h")')
  385. for dir in dirs
  386. let alldirs += s:glob_dir(dir)
  387. endfor
  388. let allowed = {}
  389. for dir in alldirs
  390. let allowed[dir] = 1
  391. endfor
  392. let todo = []
  393. let found = sort(s:glob_dir(g:plug_home))
  394. while !empty(found)
  395. let f = remove(found, 0)
  396. if !has_key(allowed, f) && isdirectory(f)
  397. call add(todo, f)
  398. call append(line('$'), '- ' . f)
  399. let found = filter(found, 'stridx(v:val, f) != 0')
  400. end
  401. endwhile
  402. normal! G
  403. redraw
  404. if empty(todo)
  405. call append(line('$'), 'Already clean.')
  406. else
  407. call inputsave()
  408. let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
  409. call inputrestore()
  410. if yes
  411. for dir in todo
  412. if isdirectory(dir)
  413. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . dir)
  414. endif
  415. endfor
  416. call append(line('$'), 'Removed.')
  417. else
  418. call append(line('$'), 'Cancelled.')
  419. endif
  420. endif
  421. normal! G
  422. endfunction
  423. function! s:upgrade()
  424. if executable('curl')
  425. let mee = shellescape(s:me)
  426. let new = shellescape(s:me . '.new')
  427. echo "Downloading ". s:plug_source
  428. redraw
  429. let mv = s:is_win ? 'move /Y' : 'mv -f'
  430. call system(printf(
  431. \ "curl -fLo %s %s && ".mv." %s %s.old && ".mv." %s %s",
  432. \ new, s:plug_source, mee, mee, new, mee))
  433. if v:shell_error == 0
  434. unlet g:loaded_plug
  435. echo "Downloaded ". s:plug_source
  436. return 1
  437. else
  438. echoerr "Error upgrading vim-plug"
  439. return 0
  440. endif
  441. else
  442. echoerr "`curl' not found"
  443. return 0
  444. endif
  445. endfunction
  446. function! s:status()
  447. call s:prepare()
  448. call append(0, 'Checking plugins')
  449. let errs = 0
  450. for [name, spec] in items(g:plugs)
  451. let err = 'OK'
  452. if isdirectory(spec.dir)
  453. execute 'cd '.spec.dir
  454. if s:git_valid(spec, 0)
  455. let branch = s:system('git rev-parse --abbrev-ref HEAD')
  456. if spec.branch != branch
  457. let err = '(x) Invalid branch: '.branch.'. Try PlugUpdate.'
  458. endif
  459. else
  460. let err = '(x) Invalid remote. Try PlugClean.'
  461. endif
  462. cd -
  463. else
  464. let err = '(x) Not found. Try PlugInstall.'
  465. endif
  466. let errs += err != 'OK'
  467. call append(2, printf('- %s: %s', name, err))
  468. call cursor(3, 1)
  469. redraw
  470. endfor
  471. call setline(1, 'Finished. '.errs.' error(s).')
  472. endfunction