plug.vim 11 KB

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