plug.vim 14 KB

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