plug.vim 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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. 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 contained
  135. syn match plugBracket /[[\]]/ containedin=plug2 contained
  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 s:progress_bar(2, cnt, total)
  244. call append(3, '- ' . 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 3, 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:progress_bar(line, cnt, total)
  313. call setline(a:line, '[' . s:lpad(repeat('=', a:cnt), a:total) . ']')
  314. endfunction
  315. function! s:git_valid(spec, cd)
  316. if isdirectory(a:spec.dir)
  317. if a:cd | execute "cd " . a:spec.dir | endif
  318. let ret = s:system("git config remote.origin.url") == a:spec.uri
  319. if a:cd | cd - | endif
  320. else
  321. let ret = 0
  322. endif
  323. return ret
  324. endfunction
  325. function! s:clean(force)
  326. call s:prepare()
  327. call append(0, 'Searching for unused plugins in '.g:plug_home)
  328. call append(1, '')
  329. " List of valid directories
  330. let dirs = []
  331. let [cnt, total] = [0, len(g:plugs)]
  332. for spec in values(g:plugs)
  333. if s:git_valid(spec, 1)
  334. call add(dirs, spec.dir)
  335. endif
  336. let cnt += 1
  337. call s:progress_bar(2, cnt, total)
  338. redraw
  339. endfor
  340. let alldirs = dirs +
  341. \ map(copy(dirs), 'fnamemodify(v:val, ":h")')
  342. for dir in dirs
  343. let alldirs += s:glob_dir(dir)
  344. endfor
  345. let allowed = {}
  346. for dir in alldirs
  347. let allowed[dir] = 1
  348. endfor
  349. let todo = []
  350. let found = sort(s:glob_dir(g:plug_home))
  351. while !empty(found)
  352. let f = remove(found, 0)
  353. if !has_key(allowed, f) && isdirectory(f)
  354. call add(todo, f)
  355. call append(line('$'), '- ' . f)
  356. let found = filter(found, 'stridx(v:val, f) != 0')
  357. end
  358. endwhile
  359. normal! G
  360. redraw
  361. if empty(todo)
  362. call append(line('$'), 'Already clean.')
  363. else
  364. call inputsave()
  365. let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
  366. call inputrestore()
  367. if yes
  368. for dir in todo
  369. if isdirectory(dir)
  370. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . dir)
  371. endif
  372. endfor
  373. call append(line('$'), 'Removed.')
  374. else
  375. call append(line('$'), 'Cancelled.')
  376. endif
  377. endif
  378. normal! G
  379. endfunction
  380. function! s:upgrade()
  381. if executable('curl')
  382. let mee = shellescape(s:me)
  383. let new = shellescape(s:me . '.new')
  384. echo "Downloading ". s:plug_source
  385. redraw
  386. let mv = s:is_win ? 'move /Y' : 'mv -f'
  387. call system(printf(
  388. \ "curl -fLo %s %s && ".mv." %s %s.old && ".mv." %s %s",
  389. \ new, s:plug_source, mee, mee, new, mee))
  390. if v:shell_error == 0
  391. unlet g:loaded_plug
  392. echo "Downloaded ". s:plug_source
  393. return 1
  394. else
  395. echoerr "Error upgrading vim-plug"
  396. return 0
  397. endif
  398. else
  399. echoerr "`curl' not found"
  400. return 0
  401. endif
  402. endfunction