plug.vim 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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 -fL -o ~/.vim/autoload/plug.vim \
  8. " https://raw.github.com/junegunn/vim-plug/master/plug.vim
  9. "
  10. " Edit your .vimrc
  11. "
  12. " call plug#init()
  13. "
  14. " Plug 'junegunn/seoul256'
  15. " " Plug 'user/repo', 'branch_or_tag'
  16. " " ...
  17. "
  18. " Then :PlugInstall to install plugins. (default: ~/.vim/plugged)
  19. " You can change the location of the plugins with plug#init(path) call.
  20. "
  21. "
  22. " Copyright (c) 2013 Junegunn Choi
  23. "
  24. " MIT License
  25. "
  26. " Permission is hereby granted, free of charge, to any person obtaining
  27. " a copy of this software and associated documentation files (the
  28. " "Software"), to deal in the Software without restriction, including
  29. " without limitation the rights to use, copy, modify, merge, publish,
  30. " distribute, sublicense, and/or sell copies of the Software, and to
  31. " permit persons to whom the Software is furnished to do so, subject to
  32. " the following conditions:
  33. "
  34. " The above copyright notice and this permission notice shall be
  35. " included in all copies or substantial portions of the Software.
  36. "
  37. " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  38. " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  39. " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  40. " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  41. " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  42. " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  43. " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  44. if exists('g:loaded_plug')
  45. finish
  46. endif
  47. let g:loaded_plug = 1
  48. let s:plug_source = 'https://raw.github.com/junegunn/vim-plug/master/vim-plug.vim'
  49. let s:plug_win = 0
  50. let s:is_win = has('win32') || has('win64')
  51. let s:me = expand('<sfile>:p')
  52. function! plug#init(...)
  53. set nocompatible
  54. filetype off
  55. filetype plugin indent on
  56. let home = a:0 > 0 ? fnamemodify(a:1, ':p') : (split(&rtp, ',')[0].'/plugged')
  57. if !isdirectory(home)
  58. try
  59. call mkdir(home, 'p')
  60. catch
  61. echoerr 'Invalid plug directory: '. home
  62. return
  63. endtry
  64. endif
  65. if !executable('git')
  66. echoerr "`git' executable not found. vim-plug requires git."
  67. return
  68. endif
  69. let g:plug_home = home
  70. let g:plug = {}
  71. command! -nargs=+ Plug call s:add(<args>)
  72. command! -nargs=* PlugInstall call s:install(<f-args>)
  73. command! -nargs=* PlugUpdate call s:update(<f-args>)
  74. command! -nargs=0 PlugClean call s:clean()
  75. command! -nargs=0 PlugUpgrade call s:upgrade()
  76. endfunction
  77. function! s:add(...)
  78. if a:0 == 1
  79. let [plugin, branch] = [a:1, 'master']
  80. elseif a:0 == 2
  81. let [plugin, branch] = a:000
  82. else
  83. echoerr "Invalid number of arguments (1..2)"
  84. return
  85. endif
  86. if plugin !~ '/'
  87. let plugin = 'vim-scripts/'. plugin
  88. endif
  89. let name = split(plugin, '/')[-1]
  90. let dir = fnamemodify(join([g:plug_home, plugin, branch], '/'), ':p')
  91. let uri = 'https://github.com/' . plugin
  92. let spec = { 'name': name, 'dir': dir, 'uri': uri, 'branch': branch }
  93. execute "set rtp+=".dir
  94. let g:plug[plugin] = spec
  95. endfunction
  96. function! s:install(...)
  97. call s:update_impl(0, a:000)
  98. endfunction
  99. function! s:update(...)
  100. call s:update_impl(1, a:000)
  101. endfunction
  102. function! s:apply()
  103. for spec in values(g:plug)
  104. let docd = join([spec.dir, 'doc'], '/')
  105. if isdirectory(docd)
  106. execute "helptags ". join([spec.dir, 'doc'], '/')
  107. endif
  108. endfor
  109. runtime! plugin/*.vim
  110. runtime! after/*.vim
  111. source $MYVIMRC
  112. endfunction
  113. function! s:syntax()
  114. syntax clear
  115. syntax region plug1 start=/\%1l/ end=/\%2l/ contains=ALL
  116. syntax region plug2 start=/\%2l/ end=/\%3l/ contains=ALL
  117. syn match plugNumber /[0-9]\+[0-9.]*/ containedin=plug1
  118. syn match plugBracket /[[\]]/ containedin=plug2
  119. syn match plugDash /^-/
  120. syn match plugName /\(^- \)\@<=[^:]*/
  121. hi def link plug1 Title
  122. hi def link plug2 Repeat
  123. hi def link plugBracket Structure
  124. hi def link plugNumber Number
  125. hi def link plugDash Special
  126. hi def link plugName Label
  127. endfunction
  128. function! s:lpad(str, len)
  129. return a:str . repeat(' ', a:len - len(a:str))
  130. endfunction
  131. function! s:system(cmd)
  132. return split(system(a:cmd), '\n')[-1]
  133. endfunction
  134. function! s:prepare()
  135. execute s:plug_win . 'wincmd w'
  136. if exists('b:plug')
  137. %d
  138. else
  139. vertical topleft new
  140. noremap <silent> <buffer> q :q<cr>
  141. let b:plug = 1
  142. let s:plug_win = winnr()
  143. call s:assign_name()
  144. endif
  145. setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline
  146. setf vim-plug
  147. call s:syntax()
  148. endfunction
  149. function! s:assign_name()
  150. " Assign buffer name
  151. let prefix = '[Plugins]'
  152. let name = prefix
  153. let idx = 2
  154. while bufexists(name)
  155. let name = printf("%s (%s)", prefix, idx)
  156. let idx = idx + 1
  157. endwhile
  158. silent! execute "f ".fnameescape(name)
  159. endfunction
  160. function! s:finish()
  161. call append(line('$'), '')
  162. call append(line('$'), 'Finishing ... ')
  163. redraw
  164. call s:apply()
  165. call s:syntax()
  166. call setline(line('$'), getline(line('$')) . 'Done!')
  167. normal! G
  168. endfunction
  169. function! s:update_impl(pull, args)
  170. if has('ruby') && get(g:, 'plug_parallel', 1)
  171. let threads = len(a:args) > 0 ? a:args[0] : len(g:plug)
  172. else
  173. let threads = 1
  174. endif
  175. call s:prepare()
  176. call append(0, 'Updating plugins')
  177. call append(1, '['. s:lpad('', len(g:plug)) .']')
  178. normal! 2G
  179. redraw
  180. if threads > 1
  181. call s:update_parallel(a:pull, threads)
  182. else
  183. call s:update_serial(a:pull)
  184. endif
  185. call s:finish()
  186. endfunction
  187. function! s:update_serial(pull)
  188. let st = reltime()
  189. let base = g:plug_home
  190. let cnt = 0
  191. let total = len(g:plug)
  192. for [name, spec] in items(g:plug)
  193. let cnt += 1
  194. let d = shellescape(spec.dir)
  195. if isdirectory(spec.dir)
  196. execute 'cd '.spec.dir
  197. let result = a:pull ? s:system('git pull 2>&1') : 'Already installed'
  198. let error = a:pull ? v:shell_error != 0 : 0
  199. else
  200. if !isdirectory(base)
  201. call mkdir(base, 'p')
  202. endif
  203. execute 'cd '.base
  204. let result = s:system(
  205. \ printf('git clone --recursive %s -b %s %s 2>&1',
  206. \ shellescape(spec.uri), shellescape(spec.branch), d))
  207. let error = v:shell_error != 0
  208. endif
  209. cd -
  210. if error
  211. let result = '(x) ' . result
  212. endif
  213. call setline(1, "Updating plugins (".cnt."/".total.")")
  214. call setline(2, '[' . s:lpad(repeat('=', cnt), total) . ']')
  215. call append(line('$'), '- ' . name . ': ' . result)
  216. normal! 2G
  217. redraw
  218. endfor
  219. call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
  220. endfunction
  221. function! s:update_parallel(pull, threads)
  222. ruby << EOF
  223. require 'fileutils'
  224. st = Time.now
  225. cd = VIM::evaluate('s:is_win').to_i == 1 ? 'cd /d' : 'cd'
  226. pull = VIM::evaluate('a:pull').to_i == 1
  227. base = VIM::evaluate('g:plug_home')
  228. all = VIM::evaluate('g:plug')
  229. total = all.length
  230. cnt = 0
  231. skip = 'Already installed'
  232. all.each_slice(VIM::evaluate('a:threads').to_i).each do |slice|
  233. slice.map { |pair|
  234. spec = pair.last
  235. Thread.new do
  236. name, dir, uri, branch = spec.values_at *%w[name dir uri branch]
  237. result =
  238. if File.directory? dir
  239. pull ? `#{cd} #{dir} && git pull 2>&1` : skip
  240. else
  241. FileUtils.mkdir_p(base)
  242. `#{cd} #{base} && git clone --recursive #{uri} -b #{branch} #{dir} 2>&1`
  243. end.lines.to_a.last.strip
  244. result = '(x) ' + result if $? != 0 && result != skip
  245. Thread.current[:result] = "- #{name}: #{result}"
  246. end
  247. }.each do |t|
  248. t.join
  249. $curbuf[1] = "Updating plugins (#{cnt += 1}/#{total})"
  250. $curbuf[2] = '[' + ('=' * cnt).ljust(total) + ']'
  251. $curbuf.append $curbuf.count, t[:result]
  252. VIM::command('normal! 2G')
  253. VIM::command('redraw')
  254. end
  255. end
  256. $curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
  257. EOF
  258. endfunction
  259. function! s:glob_dir(path)
  260. return filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)')
  261. endfunction
  262. function! s:clean()
  263. call s:prepare()
  264. call append(0, 'Removing unused plugins in '.g:plug_home)
  265. " List of files
  266. let dirs = map(values(g:plug), 'substitute(v:val.dir, "/*$", "", "")')
  267. let alldirs = dirs +
  268. \ map(copy(dirs), 'fnamemodify(v:val, ":h")') +
  269. \ map(copy(dirs), 'fnamemodify(v:val, ":h:h")')
  270. for dir in dirs
  271. let alldirs += s:glob_dir(dir)
  272. endfor
  273. let allowed = {}
  274. for dir in alldirs
  275. let allowed[dir] = 1
  276. endfor
  277. let todo = []
  278. let found = sort(s:glob_dir(g:plug_home))
  279. while !empty(found)
  280. let f = remove(found, 0)
  281. if !has_key(allowed, f) && isdirectory(f)
  282. call add(todo, f)
  283. call append(line('$'), '- ' . f)
  284. let found = filter(found, 'stridx(v:val, f) != 0')
  285. end
  286. endwhile
  287. normal! G
  288. redraw
  289. if empty(todo)
  290. call append(line('$'), 'Already clean.')
  291. else
  292. call inputsave()
  293. let yes = input("Proceed? (Y/N) ")
  294. call inputrestore()
  295. if yes =~? '^y'
  296. for dir in todo
  297. if isdirectory(dir)
  298. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . dir)
  299. endif
  300. endfor
  301. call append(line('$'), 'Removed.')
  302. else
  303. call append(line('$'), 'Cancelled.')
  304. endif
  305. endif
  306. normal! G
  307. endfunction
  308. function! s:upgrade()
  309. if executable('curl')
  310. let mee = shellescape(s:me)
  311. let new = shellescape(s:me . '.new')
  312. echo "Downloading ". s:plug_source
  313. redraw
  314. let mv = s:is_win ? 'move /Y' : 'mv -f'
  315. call system(printf(
  316. \ "curl -fL -o %s %s && ".mv." %s %s.old && ".mv." %s %s",
  317. \ new, s:plug_source, mee, mee, new, mee))
  318. if v:shell_error == 0
  319. unlet g:loaded_plug
  320. execute "source ". s:me
  321. else
  322. echoerr "Error upgrading vim-plug"
  323. return
  324. endif
  325. else
  326. echoerr "`curl' not found"
  327. return
  328. endif
  329. endfunction