plug.vim 11 KB

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