plug.vim 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107
  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.vim'
  15. " Plug 'junegunn/vim-easy-align'
  16. " Plug 'junegunn/goyo.vim', { 'on': 'Goyo' }
  17. " " Plug 'user/repo1', 'branch_or_tag'
  18. " " Plug 'user/repo2', { 'rtp': 'vim/plugin/dir', 'branch': 'branch_or_tag' }
  19. " " ...
  20. "
  21. " call plug#end()
  22. "
  23. " Then :PlugInstall to install plugins. (default: ~/.vim/plugged)
  24. " You can change the location of the plugins with plug#begin(path) call.
  25. "
  26. "
  27. " Copyright (c) 2014 Junegunn Choi
  28. "
  29. " MIT License
  30. "
  31. " Permission is hereby granted, free of charge, to any person obtaining
  32. " a copy of this software and associated documentation files (the
  33. " "Software"), to deal in the Software without restriction, including
  34. " without limitation the rights to use, copy, modify, merge, publish,
  35. " distribute, sublicense, and/or sell copies of the Software, and to
  36. " permit persons to whom the Software is furnished to do so, subject to
  37. " the following conditions:
  38. "
  39. " The above copyright notice and this permission notice shall be
  40. " included in all copies or substantial portions of the Software.
  41. "
  42. " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  43. " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  44. " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  45. " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  46. " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  47. " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  48. " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  49. if exists('g:loaded_plug')
  50. finish
  51. endif
  52. let g:loaded_plug = 1
  53. let s:cpo_save = &cpo
  54. set cpo&vim
  55. let s:plug_source = 'https://raw.github.com/junegunn/vim-plug/master/plug.vim'
  56. let s:plug_file = 'Plugfile'
  57. let s:plug_buf = -1
  58. let s:mac_gui = has('gui_macvim') && has('gui_running')
  59. let s:is_win = has('win32') || has('win64')
  60. let s:me = expand('<sfile>:p')
  61. function! plug#begin(...)
  62. if a:0 > 0
  63. let home = s:path(fnamemodify(a:1, ':p'))
  64. elseif exists('g:plug_home')
  65. let home = s:path(g:plug_home)
  66. elseif !empty(&rtp)
  67. let home = s:path(split(&rtp, ',')[0]) . '/plugged'
  68. else
  69. echoerr "Unable to determine plug home. Try calling plug#begin() with a path argument."
  70. return 0
  71. endif
  72. if !isdirectory(home)
  73. try
  74. call mkdir(home, 'p')
  75. catch
  76. echoerr 'Invalid plug directory: '. home
  77. return 0
  78. endtry
  79. endif
  80. if !executable('git')
  81. echoerr "`git' executable not found. vim-plug requires git."
  82. return 0
  83. endif
  84. let g:plug_home = home
  85. let g:plugs = {}
  86. " we want to keep track of the order plugins where registered.
  87. let g:plugs_order = []
  88. command! -nargs=+ -bar Plug call s:add(1, <args>)
  89. command! -nargs=* -complete=customlist,s:names PlugInstall call s:install(<f-args>)
  90. command! -nargs=* -complete=customlist,s:names PlugUpdate call s:update(<f-args>)
  91. command! -nargs=0 -bang PlugClean call s:clean('<bang>' == '!')
  92. command! -nargs=0 PlugUpgrade if s:upgrade() | execute "source ". s:me | endif
  93. command! -nargs=0 PlugStatus call s:status()
  94. command! -nargs=0 PlugDiff call s:diff()
  95. return 1
  96. endfunction
  97. function! s:to_a(v)
  98. return type(a:v) == 3 ? a:v : [a:v]
  99. endfunction
  100. function! plug#end()
  101. if !exists('g:plugs')
  102. echoerr 'Call plug#begin() first'
  103. return
  104. endif
  105. let keys = keys(g:plugs)
  106. while !empty(keys)
  107. let keys = keys(s:extend(keys))
  108. endwhile
  109. if exists('#PlugLOD')
  110. augroup PlugLOD
  111. autocmd!
  112. augroup END
  113. augroup! PlugLOD
  114. endif
  115. let lod = {}
  116. filetype off
  117. " we want to make sure the plugin directories are added to rtp in the same
  118. " order that they are registered with the Plug command. since the s:add_rtp
  119. " function uses ^= to add plugin directories to the front of the rtp, we
  120. " need to loop through the plugins in reverse
  121. for name in reverse(copy(g:plugs_order))
  122. let plug = g:plugs[name]
  123. if !has_key(plug, 'on') && !has_key(plug, 'for')
  124. call s:add_rtp(s:rtp(plug))
  125. continue
  126. endif
  127. if has_key(plug, 'on')
  128. let commands = s:to_a(plug.on)
  129. for cmd in commands
  130. if cmd =~ '^<Plug>.\+'
  131. if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i'))
  132. for [mode, map_prefix, key_prefix] in
  133. \ [['i', "<C-O>", ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']]
  134. execute printf(
  135. \ "%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, '%s')<CR>",
  136. \ mode, cmd, map_prefix, string(cmd), string(name), key_prefix)
  137. endfor
  138. endif
  139. elseif !exists(':'.cmd)
  140. execute printf(
  141. \ "command! -nargs=* -range -bang %s call s:lod_cmd(%s, '<bang>', <line1>, <line2>, <q-args>, %s)",
  142. \ cmd, string(cmd), string(name))
  143. endif
  144. endfor
  145. endif
  146. if has_key(plug, 'for')
  147. for vim in split(globpath(s:rtp(plug), 'ftdetect/**/*.vim'), '\n')
  148. execute 'source '.vim
  149. endfor
  150. for key in s:to_a(plug.for)
  151. if !has_key(lod, key)
  152. let lod[key] = []
  153. endif
  154. call add(lod[key], name)
  155. endfor
  156. endif
  157. endfor
  158. for [key, names] in items(lod)
  159. augroup PlugLOD
  160. execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s)',
  161. \ key, string(key), string(reverse(names)))
  162. augroup END
  163. endfor
  164. call s:reorg_rtp()
  165. filetype plugin indent on
  166. syntax on
  167. endfunction
  168. if s:is_win
  169. function! s:rtp(spec)
  170. let rtp = s:dirpath(a:spec.dir . get(a:spec, 'rtp', ''))
  171. return substitute(rtp, '\\*$', '', '')
  172. endfunction
  173. function! s:path(path)
  174. return substitute(substitute(a:path, '/', '\', 'g'), '[/\\]*$', '', '')
  175. endfunction
  176. function! s:dirpath(path)
  177. return s:path(a:path) . '\'
  178. endfunction
  179. else
  180. function! s:rtp(spec)
  181. return s:dirpath(a:spec.dir . get(a:spec, 'rtp', ''))
  182. endfunction
  183. function! s:path(path)
  184. return substitute(a:path, '[/\\]*$', '', '')
  185. endfunction
  186. function! s:dirpath(path)
  187. return s:path(a:path) . '/'
  188. endfunction
  189. endif
  190. function! s:esc(path)
  191. return substitute(a:path, ' ', '\\ ', 'g')
  192. endfunction
  193. function! s:add_rtp(rtp)
  194. execute "set rtp^=".s:esc(a:rtp)
  195. let after = globpath(a:rtp, 'after')
  196. if isdirectory(after)
  197. execute "set rtp+=".s:esc(after)
  198. endif
  199. endfunction
  200. function! s:reorg_rtp()
  201. if !empty(s:first_rtp)
  202. execute 'set rtp-='.s:first_rtp
  203. execute 'set rtp^='.s:first_rtp
  204. endif
  205. if s:last_rtp !=# s:first_rtp
  206. execute 'set rtp-='.s:last_rtp
  207. execute 'set rtp+='.s:last_rtp
  208. endif
  209. endfunction
  210. function! s:lod(plug, types)
  211. let rtp = s:rtp(a:plug)
  212. call s:add_rtp(rtp)
  213. for dir in a:types
  214. for vim in split(globpath(rtp, dir.'/**/*.vim'), '\n')
  215. execute 'source '.vim
  216. endfor
  217. endfor
  218. endfunction
  219. function! s:lod_ft(pat, names)
  220. for name in a:names
  221. call s:lod(g:plugs[name], ['plugin', 'after'])
  222. endfor
  223. call s:reorg_rtp()
  224. execute 'autocmd! PlugLOD FileType ' . a:pat
  225. silent! doautocmd filetypeplugin FileType
  226. endfunction
  227. function! s:lod_cmd(cmd, bang, l1, l2, args, name)
  228. execute 'delc '.a:cmd
  229. call s:lod(g:plugs[a:name], ['plugin', 'ftdetect', 'after'])
  230. call s:reorg_rtp()
  231. execute printf("%s%s%s %s", (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
  232. endfunction
  233. function! s:lod_map(map, name, prefix)
  234. execute 'unmap '.a:map
  235. execute 'iunmap '.a:map
  236. call s:lod(g:plugs[a:name], ['plugin', 'ftdetect', 'after'])
  237. call s:reorg_rtp()
  238. let extra = ''
  239. while 1
  240. let c = getchar(0)
  241. if c == 0
  242. break
  243. endif
  244. let extra .= nr2char(c)
  245. endwhile
  246. call feedkeys(a:prefix . substitute(a:map, '^<Plug>', "\<Plug>", '') . extra)
  247. endfunction
  248. function! s:add(force, ...)
  249. let opts = { 'branch': 'master', 'frozen': 0 }
  250. if a:0 == 1
  251. let plugin = a:1
  252. elseif a:0 == 2
  253. let plugin = a:1
  254. if type(a:2) == 1
  255. let opts.branch = a:2
  256. elseif type(a:2) == 4
  257. call extend(opts, a:2)
  258. if has_key(opts, 'tag')
  259. let opts.branch = remove(opts, 'tag')
  260. endif
  261. else
  262. echoerr "Invalid argument type (expected: string or dictionary)"
  263. return
  264. endif
  265. else
  266. echoerr "Invalid number of arguments (1..2)"
  267. return
  268. endif
  269. let plugin = substitute(plugin, '[/\\]*$', '', '')
  270. let name = substitute(split(plugin, '/')[-1], '\.git$', '', '')
  271. if !a:force && has_key(g:plugs, name)
  272. let s:extended[name] = g:plugs[name]
  273. return
  274. endif
  275. if plugin[0] =~ '[/$~]' || plugin =~? '^[a-z]:'
  276. let spec = extend(opts, { 'dir': s:dirpath(expand(plugin)) })
  277. else
  278. if plugin =~ ':'
  279. let uri = plugin
  280. else
  281. if plugin !~ '/'
  282. let plugin = 'vim-scripts/'. plugin
  283. endif
  284. let uri = 'https://git:@github.com/' . plugin . '.git'
  285. endif
  286. let dir = s:dirpath( fnamemodify(join([g:plug_home, name], '/'), ':p') )
  287. let spec = extend(opts, { 'dir': dir, 'uri': uri })
  288. endif
  289. let g:plugs[name] = spec
  290. if !a:force
  291. let s:extended[name] = spec
  292. endif
  293. let g:plugs_order += [name]
  294. endfunction
  295. function! s:install(...)
  296. call s:update_impl(0, a:000)
  297. endfunction
  298. function! s:update(...)
  299. call s:update_impl(1, a:000)
  300. endfunction
  301. function! s:apply()
  302. for spec in values(g:plugs)
  303. let docd = join([spec.dir, 'doc'], '/')
  304. if isdirectory(docd)
  305. silent! execute "helptags ". join([spec.dir, 'doc'], '/')
  306. endif
  307. endfor
  308. runtime! plugin/*.vim
  309. runtime! after/*.vim
  310. silent! source $MYVIMRC
  311. endfunction
  312. function! s:syntax()
  313. syntax clear
  314. syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber
  315. syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX
  316. syn match plugNumber /[0-9]\+[0-9.]*/ contained
  317. syn match plugBracket /[[\]]/ contained
  318. syn match plugX /x/ contained
  319. syn match plugDash /^-/
  320. syn match plugPlus /^+/
  321. syn match plugStar /^*/
  322. syn match plugName /\(^- \)\@<=[^:]*/
  323. syn match plugInstall /\(^+ \)\@<=[^:]*/
  324. syn match plugUpdate /\(^* \)\@<=[^:]*/
  325. syn match plugCommit /^ [0-9a-z]\{7} .*/ contains=plugRelDate,plugSha
  326. syn match plugSha /\(^ \)\@<=[0-9a-z]\{7}/ contained
  327. syn match plugRelDate /([^)]*)$/ contained
  328. syn match plugError /^x.*/
  329. syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
  330. hi def link plug1 Title
  331. hi def link plug2 Repeat
  332. hi def link plugX Exception
  333. hi def link plugBracket Structure
  334. hi def link plugNumber Number
  335. hi def link plugDash Special
  336. hi def link plugPlus Constant
  337. hi def link plugStar Boolean
  338. hi def link plugName Label
  339. hi def link plugInstall Function
  340. hi def link plugUpdate Type
  341. hi def link plugError Error
  342. hi def link plugRelDate Comment
  343. hi def link plugSha Identifier
  344. endfunction
  345. function! s:lpad(str, len)
  346. return a:str . repeat(' ', a:len - len(a:str))
  347. endfunction
  348. function! s:lastline(msg)
  349. let lines = split(a:msg, '\n')
  350. return get(lines, -1, '')
  351. endfunction
  352. function! s:prepare()
  353. if bufexists(s:plug_buf)
  354. let winnr = bufwinnr(s:plug_buf)
  355. if winnr < 0
  356. vertical topleft new
  357. execute 'buffer ' . s:plug_buf
  358. else
  359. execute winnr . 'wincmd w'
  360. endif
  361. silent %d _
  362. else
  363. vertical topleft new
  364. nnoremap <silent> <buffer> q :if b:plug_preview==1<bar>pc<bar>endif<bar>q<cr>
  365. nnoremap <silent> <buffer> R :silent! call <SID>retry()<cr>
  366. nnoremap <silent> <buffer> D :PlugDiff<cr>
  367. nnoremap <silent> <buffer> S :PlugStatus<cr>
  368. nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr>
  369. nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr>
  370. let b:plug_preview = -1
  371. let s:plug_buf = winbufnr(0)
  372. call s:assign_name()
  373. endif
  374. silent! unmap <buffer> <cr>
  375. setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline
  376. setf vim-plug
  377. call s:syntax()
  378. endfunction
  379. function! s:assign_name()
  380. " Assign buffer name
  381. let prefix = '[Plugins]'
  382. let name = prefix
  383. let idx = 2
  384. while bufexists(name)
  385. let name = printf("%s (%s)", prefix, idx)
  386. let idx = idx + 1
  387. endwhile
  388. silent! execute "f ".fnameescape(name)
  389. endfunction
  390. function! s:finish(pull)
  391. call append(3, '- Finishing ... ')
  392. redraw
  393. call s:apply()
  394. call s:syntax()
  395. call setline(4, getline(4) . 'Done!')
  396. normal! gg
  397. redraw
  398. let msgs = []
  399. if !empty(s:prev_update.errors)
  400. call add(msgs, "Press 'R' to retry.")
  401. endif
  402. if a:pull
  403. call add(msgs, "Press 'D' to see the updated changes.")
  404. endif
  405. echo join(msgs, ' ')
  406. endfunction
  407. function! s:retry()
  408. if empty(s:prev_update.errors)
  409. return
  410. endif
  411. call s:update_impl(s:prev_update.pull,
  412. \ extend(copy(s:prev_update.errors), [s:prev_update.threads]))
  413. endfunction
  414. function! s:is_managed(name)
  415. return has_key(g:plugs[a:name], 'uri')
  416. endfunction
  417. function! s:names(...)
  418. return filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')
  419. endfunction
  420. function! s:update_impl(pull, args) abort
  421. let args = copy(a:args)
  422. let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
  423. \ remove(args, -1) : get(g:, 'plug_threads', 16)
  424. let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
  425. let todo = empty(args) ? filter(managed, '!get(v:val, "frozen", 0)') :
  426. \ filter(managed, 'index(args, v:key) >= 0')
  427. if empty(todo)
  428. echohl WarningMsg
  429. echo 'No plugin to '. (a:pull ? 'update' : 'install') . '.'
  430. echohl None
  431. return
  432. endif
  433. call s:prepare()
  434. call append(0, a:pull ? 'Updating plugins' : 'Installing plugins')
  435. call append(1, '['. s:lpad('', len(todo)) .']')
  436. normal! 2G
  437. redraw
  438. let len = len(g:plugs)
  439. let s:prev_update = { 'errors': [], 'pull': a:pull, 'threads': threads }
  440. if has('ruby') && threads > 1
  441. try
  442. let imd = &imd
  443. if s:mac_gui
  444. set noimd
  445. endif
  446. call s:update_parallel(a:pull, todo, threads)
  447. catch
  448. let lines = getline(4, '$')
  449. let printed = {}
  450. silent 4,$d
  451. for line in lines
  452. let name = get(matchlist(line, '^. \([^:]\+\):'), 1, '')
  453. if empty(name) || !has_key(printed, name)
  454. call append('$', line)
  455. if !empty(name)
  456. let printed[name] = 1
  457. if line[0] == 'x' && index(s:prev_update.errors, name) < 0
  458. call add(s:prev_update.errors, name)
  459. end
  460. endif
  461. endif
  462. endfor
  463. finally
  464. let &imd = imd
  465. endtry
  466. else
  467. call s:update_serial(a:pull, todo)
  468. endif
  469. if len(g:plugs) > len
  470. call plug#end()
  471. endif
  472. call s:finish(a:pull)
  473. endfunction
  474. function! s:extend(names)
  475. let s:extended = {}
  476. try
  477. command! -nargs=+ Plug call s:add(0, <args>)
  478. for name in a:names
  479. let plugfile = globpath(s:rtp(g:plugs[name]), s:plug_file)
  480. if filereadable(plugfile)
  481. execute "source ". s:esc(plugfile)
  482. endif
  483. endfor
  484. finally
  485. command! -nargs=+ Plug call s:add(1, <args>)
  486. endtry
  487. return s:extended
  488. endfunction
  489. function! s:update_progress(pull, cnt, bar, total)
  490. call setline(1, (a:pull ? 'Updating' : 'Installing').
  491. \ " plugins (".a:cnt."/".a:total.")")
  492. call s:progress_bar(2, a:bar, a:total)
  493. normal! 2G
  494. redraw
  495. endfunction
  496. function! s:update_serial(pull, todo)
  497. let st = reltime()
  498. let base = g:plug_home
  499. let todo = copy(a:todo)
  500. let total = len(todo)
  501. let done = {}
  502. let bar = ''
  503. while !empty(todo)
  504. for [name, spec] in items(todo)
  505. let done[name] = 1
  506. if isdirectory(spec.dir)
  507. execute 'cd '.s:esc(spec.dir)
  508. let [valid, msg] = s:git_valid(spec, 0, 0)
  509. if valid
  510. let result = a:pull ?
  511. \ s:system(
  512. \ printf('git checkout -q %s 2>&1 && git pull origin %s 2>&1 && git submodule update --init --recursive 2>&1',
  513. \ s:shellesc(spec.branch), s:shellesc(spec.branch))) : 'Already installed'
  514. let error = a:pull ? v:shell_error != 0 : 0
  515. else
  516. let result = msg
  517. let error = 1
  518. endif
  519. cd -
  520. else
  521. if !isdirectory(base)
  522. call mkdir(base, 'p')
  523. endif
  524. let result = s:system(
  525. \ printf('git clone --recursive %s -b %s %s 2>&1 && cd %s && git submodule update --init --recursive 2>&1',
  526. \ s:shellesc(spec.uri),
  527. \ s:shellesc(spec.branch),
  528. \ s:shellesc(substitute(spec.dir, '[\/]\+$', '', '')),
  529. \ s:shellesc(spec.dir)))
  530. let error = v:shell_error != 0
  531. endif
  532. let bar .= error ? 'x' : '='
  533. if error
  534. call add(s:prev_update.errors, name)
  535. endif
  536. call append(3, s:format_message(!error, name, result))
  537. call s:update_progress(a:pull, len(done), bar, total)
  538. endfor
  539. let extended = s:extend(keys(todo))
  540. if !empty(extended)
  541. let todo = filter(extended, '!has_key(done, v:key)')
  542. let total += len(todo)
  543. call s:update_progress(a:pull, len(done), bar, total)
  544. else
  545. break
  546. endif
  547. endwhile
  548. call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
  549. endfunction
  550. function! s:update_parallel(pull, todo, threads)
  551. ruby << EOF
  552. module PlugStream
  553. SEP = ["\r", "\n", nil]
  554. def get_line
  555. buffer = ''
  556. loop do
  557. char = readchar rescue return
  558. if SEP.include? char.chr
  559. buffer << $/
  560. break
  561. else
  562. buffer << char
  563. end
  564. end
  565. buffer
  566. end
  567. end unless defined?(PlugStream)
  568. def esc arg
  569. %["#{arg.gsub('"', '\"')}"]
  570. end
  571. require 'set'
  572. require 'thread'
  573. require 'fileutils'
  574. require 'timeout'
  575. running = true
  576. st = Time.now
  577. iswin = VIM::evaluate('s:is_win').to_i == 1
  578. pull = VIM::evaluate('a:pull').to_i == 1
  579. base = VIM::evaluate('g:plug_home')
  580. all = VIM::evaluate('copy(a:todo)')
  581. limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
  582. tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1
  583. nthr = VIM::evaluate('a:threads').to_i
  584. maxy = VIM::evaluate('winheight(".")').to_i
  585. cd = iswin ? 'cd /d' : 'cd'
  586. tot = VIM::evaluate('len(a:todo)') || 0
  587. bar = ''
  588. skip = 'Already installed'
  589. mtx = Mutex.new
  590. take1 = proc { mtx.synchronize { running && all.shift } }
  591. logh = proc {
  592. cnt = bar.length
  593. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  594. $curbuf[2] = '[' + bar.ljust(tot) + ']'
  595. VIM::command('normal! 2G')
  596. VIM::command('redraw') unless iswin
  597. }
  598. where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } }
  599. log = proc { |name, result, type|
  600. mtx.synchronize do
  601. ing = ![true, false].include?(type)
  602. bar += type ? '=' : 'x' unless ing
  603. b = case type
  604. when :install then '+' when :update then '*'
  605. when true, nil then '-' else
  606. VIM::command("call add(s:prev_update.errors, '#{name}')")
  607. 'x'
  608. end
  609. result =
  610. if type || type.nil?
  611. ["#{b} #{name}: #{result.lines.to_a.last}"]
  612. elsif result =~ /^Interrupted|^Timeout/
  613. ["#{b} #{name}: #{result}"]
  614. else
  615. ["#{b} #{name}"] + result.lines.map { |l| " " << l }
  616. end
  617. if lnum = where.call(name)
  618. $curbuf.delete lnum
  619. lnum = 4 if ing && lnum > maxy
  620. end
  621. result.each_with_index do |line, offset|
  622. $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp)
  623. end
  624. logh.call
  625. end
  626. }
  627. bt = proc { |cmd, name, type|
  628. tried = timeout = 0
  629. begin
  630. tried += 1
  631. timeout += limit
  632. fd = nil
  633. data = ''
  634. if iswin
  635. Timeout::timeout(timeout) do
  636. tmp = VIM::evaluate('tempname()')
  637. system("#{cmd} > #{tmp}")
  638. data = File.read(tmp).chomp
  639. File.unlink tmp rescue nil
  640. end
  641. else
  642. fd = IO.popen(cmd).extend(PlugStream)
  643. first_line = true
  644. log_prob = 1.0 / nthr
  645. while line = Timeout::timeout(timeout) { fd.get_line }
  646. data << line
  647. log.call name, line.chomp, type if name && (first_line || rand < log_prob)
  648. first_line = false
  649. end
  650. fd.close
  651. end
  652. [$? == 0, data.chomp]
  653. rescue Timeout::Error, Interrupt => e
  654. if fd && !fd.closed?
  655. pids = [fd.pid]
  656. unless `which pgrep`.empty?
  657. children = pids
  658. until children.empty?
  659. children = children.map { |pid|
  660. `pgrep -P #{pid}`.lines.map { |l| l.chomp }
  661. }.flatten
  662. pids += children
  663. end
  664. end
  665. pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
  666. fd.close
  667. end
  668. if e.is_a?(Timeout::Error) && tried < tries
  669. 3.downto(1) do |countdown|
  670. s = countdown > 1 ? 's' : ''
  671. log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type
  672. sleep 1
  673. end
  674. log.call name, 'Retrying ...', type
  675. retry
  676. end
  677. [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
  678. end
  679. }
  680. main = Thread.current
  681. threads = []
  682. watcher = Thread.new {
  683. while VIM::evaluate('getchar(1)')
  684. sleep 0.1
  685. end
  686. mtx.synchronize do
  687. running = false
  688. threads.each { |t| t.raise Interrupt }
  689. end
  690. threads.each { |t| t.join rescue nil }
  691. main.kill
  692. }
  693. refresh = Thread.new {
  694. while true
  695. mtx.synchronize do
  696. break unless running
  697. VIM::command('noautocmd normal! a')
  698. end
  699. sleep 0.2
  700. end
  701. } if VIM::evaluate('s:mac_gui') == 1
  702. processed = Set.new
  703. progress = iswin ? '' : '--progress'
  704. until all.empty?
  705. names = all.keys
  706. processed.merge names
  707. [names.length, nthr].min.times do
  708. mtx.synchronize do
  709. threads << Thread.new {
  710. while pair = take1.call
  711. name = pair.first
  712. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  713. branch = esc branch
  714. subm = "git submodule update --init --recursive 2>&1"
  715. ok, result =
  716. if File.directory? dir
  717. dir = esc dir
  718. ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil
  719. current_uri = data.lines.to_a.last
  720. if !ret
  721. if data =~ /^Interrupted|^Timeout/
  722. [false, data]
  723. else
  724. [false, [data.chomp, "PlugClean required."].join($/)]
  725. end
  726. elsif current_uri.sub(/git:@/, '') != uri.sub(/git:@/, '')
  727. [false, ["Invalid URI: #{current_uri}",
  728. "Expected: #{uri}",
  729. "PlugClean required."].join($/)]
  730. else
  731. if pull
  732. log.call name, 'Updating ...', :update
  733. bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && (git pull origin #{branch} #{progress} 2>&1 && #{subm})", name, :update
  734. else
  735. [true, skip]
  736. end
  737. end
  738. else
  739. FileUtils.mkdir_p(base)
  740. d = esc dir.sub(%r{[\\/]+$}, '')
  741. log.call name, 'Installing ...', :install
  742. bt.call "(git clone #{progress} --recursive #{uri} -b #{branch} #{d} 2>&1 && cd #{esc dir} && #{subm})", name, :install
  743. end
  744. log.call name, result, ok
  745. end
  746. } if running
  747. end
  748. end
  749. threads.each { |t| t.join rescue nil }
  750. mtx.synchronize { threads.clear }
  751. extended = Hash[(VIM::evaluate("s:extend(#{names.inspect})") || {}).reject { |k, _|
  752. processed.include? k
  753. }]
  754. tot += extended.length
  755. all.merge!(extended)
  756. logh.call
  757. end
  758. refresh.kill if refresh
  759. watcher.kill
  760. $curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
  761. EOF
  762. endfunction
  763. function! s:shellesc(arg)
  764. return '"'.substitute(a:arg, '"', '\\"', 'g').'"'
  765. endfunction
  766. function! s:glob_dir(path)
  767. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
  768. endfunction
  769. function! s:progress_bar(line, bar, total)
  770. call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
  771. endfunction
  772. function! s:compare_git_uri(a, b)
  773. let a = substitute(a:a, 'git:@', '', '')
  774. let b = substitute(a:b, 'git:@', '', '')
  775. return a ==# b
  776. endfunction
  777. function! s:format_message(ok, name, message)
  778. if a:ok
  779. return [printf('- %s: %s', a:name, s:lastline(a:message))]
  780. else
  781. let lines = map(split(a:message, '\n'), '" ".v:val')
  782. return extend([printf('x %s:', a:name)], lines)
  783. endif
  784. endfunction
  785. function! s:system(cmd)
  786. return system(s:is_win ? '('.a:cmd.')' : a:cmd)
  787. endfunction
  788. function! s:system_chomp(str)
  789. let ret = s:system(a:str)
  790. return v:shell_error ? '' : substitute(ret, '\n$', '', '')
  791. endfunction
  792. function! s:git_valid(spec, check_branch, cd)
  793. let ret = 1
  794. let msg = 'OK'
  795. if isdirectory(a:spec.dir)
  796. if a:cd | execute "cd " . s:esc(a:spec.dir) | endif
  797. let result = split(s:system("git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url"), '\n')
  798. let remote = result[-1]
  799. if v:shell_error
  800. let msg = join([remote, "PlugClean required."], "\n")
  801. let ret = 0
  802. elseif !s:compare_git_uri(remote, a:spec.uri)
  803. let msg = join(['Invalid URI: '.remote,
  804. \ 'Expected: '.a:spec.uri,
  805. \ "PlugClean required."], "\n")
  806. let ret = 0
  807. elseif a:check_branch
  808. let branch = result[0]
  809. if a:spec.branch !=# branch
  810. let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1')
  811. if a:spec.branch !=# tag
  812. let msg = printf('Invalid branch/tag: %s (expected: %s). Try PlugUpdate.',
  813. \ (empty(tag) ? branch : tag), a:spec.branch)
  814. let ret = 0
  815. endif
  816. endif
  817. endif
  818. if a:cd | cd - | endif
  819. else
  820. let msg = 'Not found'
  821. let ret = 0
  822. endif
  823. return [ret, msg]
  824. endfunction
  825. function! s:clean(force)
  826. call s:prepare()
  827. call append(0, 'Searching for unused plugins in '.g:plug_home)
  828. call append(1, '')
  829. " List of valid directories
  830. let dirs = []
  831. let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
  832. let [cnt, total] = [0, len(managed)]
  833. for spec in values(managed)
  834. if s:git_valid(spec, 0, 1)[0]
  835. call add(dirs, spec.dir)
  836. endif
  837. let cnt += 1
  838. call s:progress_bar(2, repeat('=', cnt), total)
  839. normal! 2G
  840. redraw
  841. endfor
  842. let allowed = {}
  843. for dir in dirs
  844. let allowed[dir] = 1
  845. for child in s:glob_dir(dir)
  846. let allowed[child] = 1
  847. endfor
  848. endfor
  849. let todo = []
  850. let found = sort(s:glob_dir(g:plug_home))
  851. while !empty(found)
  852. let f = remove(found, 0)
  853. if !has_key(allowed, f) && isdirectory(f)
  854. call add(todo, f)
  855. call append(line('$'), '- ' . f)
  856. let found = filter(found, 'stridx(v:val, f) != 0')
  857. end
  858. endwhile
  859. normal! G
  860. redraw
  861. if empty(todo)
  862. call append(line('$'), 'Already clean.')
  863. else
  864. call inputsave()
  865. let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
  866. call inputrestore()
  867. if yes
  868. for dir in todo
  869. if isdirectory(dir)
  870. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(dir))
  871. endif
  872. endfor
  873. call append(line('$'), 'Removed.')
  874. else
  875. call append(line('$'), 'Cancelled.')
  876. endif
  877. endif
  878. normal! G
  879. endfunction
  880. function! s:upgrade()
  881. if executable('curl')
  882. let mee = s:shellesc(s:me)
  883. let new = s:shellesc(s:me . '.new')
  884. echo "Downloading ". s:plug_source
  885. redraw
  886. let mv = s:is_win ? 'move /Y' : 'mv -f'
  887. let cp = s:is_win ? 'copy /Y' : 'cp -f'
  888. call system(printf(
  889. \ "curl -fLo %s %s && ".cp." %s %s.old && ".mv." %s %s",
  890. \ new, s:plug_source, mee, mee, new, mee))
  891. if v:shell_error == 0
  892. unlet g:loaded_plug
  893. echo "Downloaded ". s:plug_source
  894. return 1
  895. else
  896. echoerr "Error upgrading vim-plug"
  897. return 0
  898. endif
  899. elseif has('ruby')
  900. echo "Downloading ". s:plug_source
  901. ruby << EOF
  902. require 'open-uri'
  903. require 'fileutils'
  904. me = VIM::evaluate('s:me')
  905. old = me + '.old'
  906. new = me + '.new'
  907. File.open(new, 'w') do |f|
  908. f << open(VIM::evaluate('s:plug_source')).read
  909. end
  910. FileUtils.cp me, old
  911. File.rename new, me
  912. EOF
  913. unlet g:loaded_plug
  914. echo "Downloaded ". s:plug_source
  915. return 1
  916. else
  917. echoerr "curl executable or ruby support not found"
  918. return 0
  919. endif
  920. endfunction
  921. function! s:status()
  922. call s:prepare()
  923. call append(0, 'Checking plugins')
  924. call append(1, '')
  925. let ecnt = 0
  926. let [cnt, total] = [0, len(g:plugs)]
  927. for [name, spec] in items(g:plugs)
  928. if has_key(spec, 'uri')
  929. if isdirectory(spec.dir)
  930. let [valid, msg] = s:git_valid(spec, 1, 1)
  931. else
  932. let [valid, msg] = [0, 'Not found. Try PlugInstall.']
  933. endif
  934. else
  935. if isdirectory(spec.dir)
  936. let [valid, msg] = [1, 'OK']
  937. else
  938. let [valid, msg] = [0, 'Not found.']
  939. endif
  940. endif
  941. let cnt += 1
  942. let ecnt += !valid
  943. call s:progress_bar(2, repeat('=', cnt), total)
  944. call append(3, s:format_message(valid, name, msg))
  945. normal! 2G
  946. redraw
  947. endfor
  948. call setline(1, 'Finished. '.ecnt.' error(s).')
  949. normal! gg
  950. endfunction
  951. function! s:is_preview_window_open()
  952. silent! wincmd P
  953. if &previewwindow
  954. wincmd p
  955. return 1
  956. endif
  957. return 0
  958. endfunction
  959. function! s:preview_commit()
  960. if b:plug_preview < 0
  961. let b:plug_preview = !s:is_preview_window_open()
  962. endif
  963. let sha = matchstr(getline('.'), '\(^ \)\@<=[0-9a-z]\{7}')
  964. if !empty(sha)
  965. let lnum = line('.')
  966. while lnum > 1
  967. let lnum -= 1
  968. let line = getline(lnum)
  969. let name = matchstr(line, '\(^- \)\@<=[^:]\+')
  970. if !empty(name)
  971. let dir = g:plugs[name].dir
  972. if isdirectory(dir)
  973. execute 'cd '.s:esc(dir)
  974. execute 'pedit '.sha
  975. wincmd P
  976. setlocal filetype=git buftype=nofile nobuflisted
  977. execute 'silent read !git show '.sha
  978. normal! ggdd
  979. wincmd p
  980. cd -
  981. endif
  982. break
  983. endif
  984. endwhile
  985. endif
  986. endfunction
  987. function! s:section(flags)
  988. call search('\(^- \)\@<=.', a:flags)
  989. endfunction
  990. function! s:diff()
  991. call s:prepare()
  992. call append(0, 'Collecting updated changes ...')
  993. normal! gg
  994. redraw
  995. let cnt = 0
  996. for [k, v] in items(g:plugs)
  997. if !isdirectory(v.dir) || !s:is_managed(k)
  998. continue
  999. endif
  1000. execute 'cd '.s:esc(v.dir)
  1001. let diff = system('git log --pretty=format:"%h %s (%cr)" "HEAD@{0}...HEAD@{1}"')
  1002. if !v:shell_error && !empty(diff)
  1003. call append(1, '')
  1004. call append(2, '- '.k.':')
  1005. call append(3, map(split(diff, '\n'), '" ". v:val'))
  1006. let cnt += 1
  1007. normal! gg
  1008. redraw
  1009. endif
  1010. cd -
  1011. endfor
  1012. call setline(1, cnt == 0 ? 'No updates.' : 'Last update:')
  1013. nnoremap <silent> <buffer> <cr> :silent! call <SID>preview_commit()<cr>
  1014. normal! gg
  1015. endfunction
  1016. let s:first_rtp = s:esc(get(split(&rtp, ','), 0, ''))
  1017. let s:last_rtp = s:esc(get(split(&rtp, ','), -1, ''))
  1018. let &cpo = s:cpo_save
  1019. unlet s:cpo_save