plug.vim 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  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_win = 0
  53. let s:is_win = has('win32') || has('win64')
  54. let s:me = expand('<sfile>:p')
  55. function! plug#begin(...)
  56. let home = a:0 > 0 ? fnamemodify(a:1, ':p') :
  57. \ get(g:, 'plug_home', split(&rtp, ',')[0].'/plugged')
  58. if !isdirectory(home)
  59. try
  60. call mkdir(home, 'p')
  61. catch
  62. echoerr 'Invalid plug directory: '. home
  63. return
  64. endtry
  65. endif
  66. if !executable('git')
  67. echoerr "`git' executable not found. vim-plug requires git."
  68. return
  69. endif
  70. let g:plug_home = home
  71. let g:plugs = {}
  72. command! -nargs=+ Plug call s:add(<args>)
  73. command! -nargs=* PlugInstall call s:install(<f-args>)
  74. command! -nargs=* PlugUpdate call s:update(<f-args>)
  75. command! -nargs=0 -bang PlugClean call s:clean('<bang>' == '!')
  76. command! -nargs=0 PlugUpgrade if s:upgrade() | execute "source ". s:me | endif
  77. command! -nargs=0 PlugStatus call s:status()
  78. endfunction
  79. function! plug#end()
  80. set nocompatible
  81. filetype off
  82. for plug in values(g:plugs)
  83. let dir = plug.dir
  84. execute "set rtp^=".dir
  85. if isdirectory(dir.'after')
  86. execute "set rtp+=".dir.'after'
  87. endif
  88. endfor
  89. filetype plugin indent on
  90. syntax on
  91. endfunction
  92. function! s:add(...)
  93. if a:0 == 1
  94. let [plugin, branch] = [a:1, 'master']
  95. elseif a:0 == 2
  96. let [plugin, branch] = a:000
  97. else
  98. echoerr "Invalid number of arguments (1..2)"
  99. return
  100. endif
  101. if plugin =~ ':'
  102. let uri = plugin
  103. else
  104. if plugin !~ '/'
  105. let plugin = 'vim-scripts/'. plugin
  106. endif
  107. let uri = 'https://git:@github.com/' . plugin . '.git'
  108. endif
  109. let name = substitute(split(plugin, '/')[-1], '\.git$', '', '')
  110. let dir = fnamemodify(join([g:plug_home, name], '/'), ':p')
  111. let spec = { 'dir': dir, 'uri': uri, 'branch': branch }
  112. let g:plugs[name] = spec
  113. endfunction
  114. function! s:install(...)
  115. call s:update_impl(0, a:000)
  116. endfunction
  117. function! s:update(...)
  118. call s:update_impl(1, a:000)
  119. endfunction
  120. function! s:apply()
  121. for spec in values(g:plugs)
  122. let docd = join([spec.dir, 'doc'], '/')
  123. if isdirectory(docd)
  124. execute "helptags ". join([spec.dir, 'doc'], '/')
  125. endif
  126. endfor
  127. runtime! plugin/*.vim
  128. runtime! after/*.vim
  129. silent! source $MYVIMRC
  130. endfunction
  131. function! s:syntax()
  132. syntax clear
  133. syntax region plug1 start=/\%1l/ end=/\%2l/ contains=ALL
  134. syntax region plug2 start=/\%2l/ end=/\%3l/ contains=ALL
  135. syn match plugNumber /[0-9]\+[0-9.]*/ containedin=plug1 contained
  136. syn match plugBracket /[[\]]/ containedin=plug2 contained
  137. syn match plugDash /^-/
  138. syn match plugName /\(^- \)\@<=[^:]*/
  139. syn match plugError /^- [^:]\+: (x).*/
  140. hi def link plug1 Title
  141. hi def link plug2 Repeat
  142. hi def link plugBracket Structure
  143. hi def link plugNumber Number
  144. hi def link plugDash Special
  145. hi def link plugName Label
  146. hi def link plugError Error
  147. endfunction
  148. function! s:lpad(str, len)
  149. return a:str . repeat(' ', a:len - len(a:str))
  150. endfunction
  151. function! s:system(cmd)
  152. let lines = split(system(a:cmd), '\n')
  153. return get(lines, -1, '')
  154. endfunction
  155. function! s:prepare()
  156. execute s:plug_win . 'wincmd w'
  157. if exists('b:plug')
  158. %d
  159. else
  160. vertical topleft new
  161. noremap <silent> <buffer> q :q<cr>
  162. let b:plug = 1
  163. let s:plug_win = winnr()
  164. call s:assign_name()
  165. endif
  166. setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline
  167. setf vim-plug
  168. call s:syntax()
  169. endfunction
  170. function! s:assign_name()
  171. " Assign buffer name
  172. let prefix = '[Plugins]'
  173. let name = prefix
  174. let idx = 2
  175. while bufexists(name)
  176. let name = printf("%s (%s)", prefix, idx)
  177. let idx = idx + 1
  178. endwhile
  179. silent! execute "f ".fnameescape(name)
  180. endfunction
  181. function! s:finish()
  182. call append(line('$'), '')
  183. call append(line('$'), 'Finishing ... ')
  184. redraw
  185. call s:apply()
  186. call s:syntax()
  187. call setline(line('$'), getline(line('$')) . 'Done!')
  188. normal! G
  189. endfunction
  190. function! s:update_impl(pull, args)
  191. if has('ruby') && get(g:, 'plug_parallel', 1)
  192. let threads = min(
  193. \ [len(g:plugs), len(a:args) > 0 ? a:args[0] : get(g:, 'plug_threads', 16)])
  194. else
  195. let threads = 1
  196. endif
  197. call s:prepare()
  198. call append(0, a:pull ? 'Updating plugins' : 'Installing plugins')
  199. call append(1, '['. s:lpad('', len(g:plugs)) .']')
  200. normal! 2G
  201. redraw
  202. if threads > 1
  203. call s:update_parallel(a:pull, threads)
  204. else
  205. call s:update_serial(a:pull)
  206. endif
  207. call s:finish()
  208. endfunction
  209. function! s:update_serial(pull)
  210. let st = reltime()
  211. let base = g:plug_home
  212. let cnt = 0
  213. let total = len(g:plugs)
  214. for [name, spec] in items(g:plugs)
  215. let cnt += 1
  216. let d = shellescape(spec.dir)
  217. if isdirectory(spec.dir)
  218. execute 'cd '.spec.dir
  219. if s:git_valid(spec, 0)
  220. let result = a:pull ?
  221. \ s:system(
  222. \ printf('git checkout -q %s && git pull origin %s 2>&1',
  223. \ spec.branch, spec.branch)) : 'Already installed'
  224. let error = a:pull ? v:shell_error != 0 : 0
  225. else
  226. let result = "PlugClean required. Invalid remote."
  227. let error = 1
  228. endif
  229. else
  230. if !isdirectory(base)
  231. call mkdir(base, 'p')
  232. endif
  233. execute 'cd '.base
  234. let result = s:system(
  235. \ printf('git clone --recursive %s -b %s %s 2>&1',
  236. \ shellescape(spec.uri), shellescape(spec.branch), d))
  237. let error = v:shell_error != 0
  238. endif
  239. cd -
  240. if error
  241. let result = '(x) ' . result
  242. endif
  243. call setline(1, "Updating plugins (".cnt."/".total.")")
  244. call s:progress_bar(2, cnt, total)
  245. call append(3, '- ' . name . ': ' . result)
  246. normal! 2G
  247. redraw
  248. endfor
  249. call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
  250. endfunction
  251. function! s:update_parallel(pull, threads)
  252. ruby << EOF
  253. require 'thread'
  254. require 'fileutils'
  255. st = Time.now
  256. cd = VIM::evaluate('s:is_win').to_i == 1 ? 'cd /d' : 'cd'
  257. pull = VIM::evaluate('a:pull').to_i == 1
  258. base = VIM::evaluate('g:plug_home')
  259. all = VIM::evaluate('g:plugs')
  260. total = all.length
  261. cnt = 0
  262. skip = 'Already installed'
  263. mtx = Mutex.new
  264. take1 = proc { mtx.synchronize { all.shift } }
  265. log = proc { |name, result, ok|
  266. mtx.synchronize {
  267. result = '(x) ' + result unless ok
  268. result = "- #{name}: #{result}"
  269. $curbuf[1] = "Updating plugins (#{cnt += 1}/#{total})"
  270. $curbuf[2] = '[' + ('=' * cnt).ljust(total) + ']'
  271. $curbuf.append 3, result
  272. VIM::command('normal! 2G')
  273. VIM::command('redraw')
  274. }
  275. }
  276. VIM::evaluate('a:threads').to_i.times.map { |i|
  277. Thread.new(i) do |ii|
  278. while pair = take1.call
  279. name = pair.first
  280. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  281. ok, result =
  282. if File.directory? dir
  283. current_uri = `#{cd} #{dir} && git config remote.origin.url`.chomp
  284. if $? == 0 && current_uri == uri
  285. if pull
  286. [true, `#{cd} #{dir} && git checkout -q #{branch} && git pull origin #{branch} 2>&1`]
  287. else
  288. [true, skip]
  289. end
  290. else
  291. [false, "PlugClean required. Invalid remote."]
  292. end
  293. else
  294. FileUtils.mkdir_p(base)
  295. r = `#{cd} #{base} && git clone --recursive #{uri} -b #{branch} #{dir} 2>&1`
  296. [$? == 0, r]
  297. end
  298. result = result.lines.to_a.last.strip
  299. log.call name, result, ok
  300. end
  301. end
  302. }.each(&:join)
  303. $curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
  304. EOF
  305. endfunction
  306. function! s:path(path)
  307. return substitute(s:is_win ? substitute(a:path, '/', '\', 'g') : a:path,
  308. \ '[/\\]*$', '', '')
  309. endfunction
  310. function! s:glob_dir(path)
  311. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:path(v:val)')
  312. endfunction
  313. function! s:progress_bar(line, cnt, total)
  314. call setline(a:line, '[' . s:lpad(repeat('=', a:cnt), a:total) . ']')
  315. endfunction
  316. function! s:git_valid(spec, cd)
  317. if isdirectory(a:spec.dir)
  318. if a:cd | execute "cd " . a:spec.dir | endif
  319. let ret = s:system("git config remote.origin.url") == a:spec.uri
  320. if a:cd | cd - | endif
  321. else
  322. let ret = 0
  323. endif
  324. return ret
  325. endfunction
  326. function! s:clean(force)
  327. call s:prepare()
  328. call append(0, 'Searching for unused plugins in '.g:plug_home)
  329. call append(1, '')
  330. " List of valid directories
  331. let dirs = []
  332. let [cnt, total] = [0, len(g:plugs)]
  333. for spec in values(g:plugs)
  334. if s:git_valid(spec, 1)
  335. call add(dirs, spec.dir)
  336. endif
  337. let cnt += 1
  338. call s:progress_bar(2, cnt, total)
  339. redraw
  340. endfor
  341. let alldirs = dirs +
  342. \ map(copy(dirs), 'fnamemodify(v:val, ":h")')
  343. for dir in dirs
  344. let alldirs += s:glob_dir(dir)
  345. endfor
  346. let allowed = {}
  347. for dir in alldirs
  348. let allowed[dir] = 1
  349. endfor
  350. let todo = []
  351. let found = sort(s:glob_dir(g:plug_home))
  352. while !empty(found)
  353. let f = remove(found, 0)
  354. if !has_key(allowed, f) && isdirectory(f)
  355. call add(todo, f)
  356. call append(line('$'), '- ' . f)
  357. let found = filter(found, 'stridx(v:val, f) != 0')
  358. end
  359. endwhile
  360. normal! G
  361. redraw
  362. if empty(todo)
  363. call append(line('$'), 'Already clean.')
  364. else
  365. call inputsave()
  366. let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
  367. call inputrestore()
  368. if yes
  369. for dir in todo
  370. if isdirectory(dir)
  371. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . dir)
  372. endif
  373. endfor
  374. call append(line('$'), 'Removed.')
  375. else
  376. call append(line('$'), 'Cancelled.')
  377. endif
  378. endif
  379. normal! G
  380. endfunction
  381. function! s:upgrade()
  382. if executable('curl')
  383. let mee = shellescape(s:me)
  384. let new = shellescape(s:me . '.new')
  385. echo "Downloading ". s:plug_source
  386. redraw
  387. let mv = s:is_win ? 'move /Y' : 'mv -f'
  388. call system(printf(
  389. \ "curl -fLo %s %s && ".mv." %s %s.old && ".mv." %s %s",
  390. \ new, s:plug_source, mee, mee, new, mee))
  391. if v:shell_error == 0
  392. unlet g:loaded_plug
  393. echo "Downloaded ". s:plug_source
  394. return 1
  395. else
  396. echoerr "Error upgrading vim-plug"
  397. return 0
  398. endif
  399. else
  400. echoerr "`curl' not found"
  401. return 0
  402. endif
  403. endfunction
  404. function! s:status()
  405. call s:prepare()
  406. call append(0, 'Checking plugins')
  407. let errs = 0
  408. for [name, spec] in items(g:plugs)
  409. let err = 'OK'
  410. if isdirectory(spec.dir)
  411. execute 'cd '.spec.dir
  412. if s:git_valid(spec, 0)
  413. let branch = s:system('git rev-parse --abbrev-ref HEAD')
  414. if spec.branch != branch
  415. let err = '(x) Invalid branch: '.branch.'. Try PlugUpdate.'
  416. endif
  417. else
  418. let err = '(x) Invalid remote. Try PlugClean.'
  419. endif
  420. cd -
  421. else
  422. let err = '(x) Not found. Try PlugInstall.'
  423. endif
  424. let errs += err != 'OK'
  425. call append(2, printf('- %s: %s', name, err))
  426. call cursor(3, 1)
  427. redraw
  428. endfor
  429. call setline(1, 'Finished. '.errs.' error(s).')
  430. endfunction