plug.vim 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103
  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. let printed[name] = 1
  455. call append('$', line)
  456. endif
  457. endfor
  458. echoerr v:exception
  459. finally
  460. let &imd = imd
  461. endtry
  462. else
  463. call s:update_serial(a:pull, todo)
  464. endif
  465. if len(g:plugs) > len
  466. call plug#end()
  467. endif
  468. call s:finish(a:pull)
  469. endfunction
  470. function! s:extend(names)
  471. let s:extended = {}
  472. try
  473. command! -nargs=+ Plug call s:add(0, <args>)
  474. for name in a:names
  475. let plugfile = globpath(s:rtp(g:plugs[name]), s:plug_file)
  476. if filereadable(plugfile)
  477. execute "source ". s:esc(plugfile)
  478. endif
  479. endfor
  480. finally
  481. command! -nargs=+ Plug call s:add(1, <args>)
  482. endtry
  483. return s:extended
  484. endfunction
  485. function! s:update_progress(pull, cnt, bar, total)
  486. call setline(1, (a:pull ? 'Updating' : 'Installing').
  487. \ " plugins (".a:cnt."/".a:total.")")
  488. call s:progress_bar(2, a:bar, a:total)
  489. normal! 2G
  490. redraw
  491. endfunction
  492. function! s:update_serial(pull, todo)
  493. let st = reltime()
  494. let base = g:plug_home
  495. let todo = copy(a:todo)
  496. let total = len(todo)
  497. let done = {}
  498. let bar = ''
  499. while !empty(todo)
  500. for [name, spec] in items(todo)
  501. let done[name] = 1
  502. if isdirectory(spec.dir)
  503. execute 'cd '.s:esc(spec.dir)
  504. let [valid, msg] = s:git_valid(spec, 0, 0)
  505. if valid
  506. let result = a:pull ?
  507. \ s:system(
  508. \ printf('git checkout -q %s 2>&1 && git pull origin %s 2>&1 && git submodule update --init --recursive 2>&1',
  509. \ s:shellesc(spec.branch), s:shellesc(spec.branch))) : 'Already installed'
  510. let error = a:pull ? v:shell_error != 0 : 0
  511. else
  512. let result = msg
  513. let error = 1
  514. endif
  515. cd -
  516. else
  517. if !isdirectory(base)
  518. call mkdir(base, 'p')
  519. endif
  520. let result = s:system(
  521. \ printf('git clone --recursive %s -b %s %s 2>&1 && cd %s && git submodule update --init --recursive 2>&1',
  522. \ s:shellesc(spec.uri),
  523. \ s:shellesc(spec.branch),
  524. \ s:shellesc(substitute(spec.dir, '[\/]\+$', '', '')),
  525. \ s:shellesc(spec.dir)))
  526. let error = v:shell_error != 0
  527. endif
  528. let bar .= error ? 'x' : '='
  529. if error
  530. call add(s:prev_update.errors, name)
  531. endif
  532. call append(3, s:format_message(!error, name, result))
  533. call s:update_progress(a:pull, len(done), bar, total)
  534. endfor
  535. let extended = s:extend(keys(todo))
  536. if !empty(extended)
  537. let todo = filter(extended, '!has_key(done, v:key)')
  538. let total += len(todo)
  539. call s:update_progress(a:pull, len(done), bar, total)
  540. else
  541. break
  542. endif
  543. endwhile
  544. call setline(1, "Updated. Elapsed time: " . split(reltimestr(reltime(st)))[0] . ' sec.')
  545. endfunction
  546. function! s:update_parallel(pull, todo, threads)
  547. ruby << EOF
  548. module PlugStream
  549. SEP = ["\r", "\n", nil]
  550. def get_line
  551. buffer = ''
  552. loop do
  553. char = readchar rescue return
  554. if SEP.include? char.chr
  555. buffer << $/
  556. break
  557. else
  558. buffer << char
  559. end
  560. end
  561. buffer
  562. end
  563. end unless defined?(PlugStream)
  564. def esc arg
  565. %["#{arg.gsub('"', '\"')}"]
  566. end
  567. require 'set'
  568. require 'thread'
  569. require 'fileutils'
  570. require 'timeout'
  571. running = true
  572. st = Time.now
  573. iswin = VIM::evaluate('s:is_win').to_i == 1
  574. pull = VIM::evaluate('a:pull').to_i == 1
  575. base = VIM::evaluate('g:plug_home')
  576. all = VIM::evaluate('copy(a:todo)')
  577. limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
  578. tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1
  579. nthr = VIM::evaluate('a:threads').to_i
  580. maxy = VIM::evaluate('winheight(".")').to_i
  581. cd = iswin ? 'cd /d' : 'cd'
  582. tot = VIM::evaluate('len(a:todo)') || 0
  583. bar = ''
  584. skip = 'Already installed'
  585. mtx = Mutex.new
  586. take1 = proc { mtx.synchronize { running && all.shift } }
  587. logh = proc {
  588. cnt = bar.length
  589. $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
  590. $curbuf[2] = '[' + bar.ljust(tot) + ']'
  591. VIM::command('normal! 2G')
  592. VIM::command('redraw') unless iswin
  593. }
  594. where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } }
  595. log = proc { |name, result, type|
  596. mtx.synchronize do
  597. ing = ![true, false].include?(type)
  598. bar += type ? '=' : 'x' unless ing
  599. b = case type
  600. when :install then '+' when :update then '*'
  601. when true, nil then '-' else
  602. VIM::command("call add(s:prev_update.errors, '#{name}')")
  603. 'x'
  604. end
  605. result =
  606. if type || type.nil?
  607. ["#{b} #{name}: #{result.lines.to_a.last}"]
  608. elsif result =~ /^Interrupted|^Timeout/
  609. ["#{b} #{name}: #{result}"]
  610. else
  611. ["#{b} #{name}"] + result.lines.map { |l| " " << l }
  612. end
  613. if lnum = where.call(name)
  614. $curbuf.delete lnum
  615. lnum = 4 if ing && lnum > maxy
  616. end
  617. result.each_with_index do |line, offset|
  618. $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp)
  619. end
  620. logh.call
  621. end
  622. }
  623. bt = proc { |cmd, name, type|
  624. tried = timeout = 0
  625. begin
  626. tried += 1
  627. timeout += limit
  628. fd = nil
  629. data = ''
  630. if iswin
  631. Timeout::timeout(timeout) do
  632. tmp = VIM::evaluate('tempname()')
  633. system("#{cmd} > #{tmp}")
  634. data = File.read(tmp).chomp
  635. File.unlink tmp rescue nil
  636. end
  637. else
  638. fd = IO.popen(cmd).extend(PlugStream)
  639. first_line = true
  640. log_prob = 1.0 / nthr
  641. while line = Timeout::timeout(timeout) { fd.get_line }
  642. data << line
  643. log.call name, line.chomp, type if name && (first_line || rand < log_prob)
  644. first_line = false
  645. end
  646. fd.close
  647. end
  648. [$? == 0, data.chomp]
  649. rescue Timeout::Error, Interrupt => e
  650. if fd && !fd.closed?
  651. pids = [fd.pid]
  652. unless `which pgrep`.empty?
  653. children = pids
  654. until children.empty?
  655. children = children.map { |pid|
  656. `pgrep -P #{pid}`.lines.map { |l| l.chomp }
  657. }.flatten
  658. pids += children
  659. end
  660. end
  661. pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
  662. fd.close
  663. end
  664. if e.is_a?(Timeout::Error) && tried < tries
  665. 3.downto(1) do |countdown|
  666. s = countdown > 1 ? 's' : ''
  667. log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type
  668. sleep 1
  669. end
  670. log.call name, 'Retrying ...', type
  671. retry
  672. end
  673. [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
  674. end
  675. }
  676. main = Thread.current
  677. threads = []
  678. watcher = Thread.new {
  679. while VIM::evaluate('getchar(1)')
  680. sleep 0.1
  681. end
  682. mtx.synchronize do
  683. running = false
  684. threads.each { |t| t.raise Interrupt }
  685. end
  686. threads.each { |t| t.join rescue nil }
  687. main.kill
  688. }
  689. refresh = Thread.new {
  690. while true
  691. mtx.synchronize do
  692. break unless running
  693. VIM::command('noautocmd normal! a')
  694. end
  695. sleep 0.2
  696. end
  697. } if VIM::evaluate('s:mac_gui') == 1
  698. processed = Set.new
  699. progress = iswin ? '' : '--progress'
  700. until all.empty?
  701. names = all.keys
  702. processed.merge names
  703. [names.length, nthr].min.times do
  704. mtx.synchronize do
  705. threads << Thread.new {
  706. while pair = take1.call
  707. name = pair.first
  708. dir, uri, branch = pair.last.values_at *%w[dir uri branch]
  709. branch = esc branch
  710. subm = "git submodule update --init --recursive 2>&1"
  711. ok, result =
  712. if File.directory? dir
  713. dir = esc dir
  714. ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil
  715. current_uri = data.lines.to_a.last
  716. if !ret
  717. if data =~ /^Interrupted|^Timeout/
  718. [false, data]
  719. else
  720. [false, [data.chomp, "PlugClean required."].join($/)]
  721. end
  722. elsif current_uri.sub(/git:@/, '') != uri.sub(/git:@/, '')
  723. [false, ["Invalid URI: #{current_uri}",
  724. "Expected: #{uri}",
  725. "PlugClean required."].join($/)]
  726. else
  727. if pull
  728. log.call name, 'Updating ...', :update
  729. bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && (git pull origin #{branch} #{progress} 2>&1 && #{subm})", name, :update
  730. else
  731. [true, skip]
  732. end
  733. end
  734. else
  735. FileUtils.mkdir_p(base)
  736. d = esc dir.sub(%r{[\\/]+$}, '')
  737. log.call name, 'Installing ...', :install
  738. bt.call "(git clone #{progress} --recursive #{uri} -b #{branch} #{d} 2>&1 && cd #{esc dir} && #{subm})", name, :install
  739. end
  740. log.call name, result, ok
  741. end
  742. } if running
  743. end
  744. end
  745. threads.each { |t| t.join rescue nil }
  746. mtx.synchronize { threads.clear }
  747. extended = Hash[(VIM::evaluate("s:extend(#{names.inspect})") || {}).reject { |k, _|
  748. processed.include? k
  749. }]
  750. tot += extended.length
  751. all.merge!(extended)
  752. logh.call
  753. end
  754. refresh.kill if refresh
  755. watcher.kill
  756. $curbuf[1] = "Updated. Elapsed time: #{"%.6f" % (Time.now - st)} sec."
  757. EOF
  758. endfunction
  759. function! s:shellesc(arg)
  760. return '"'.substitute(a:arg, '"', '\\"', 'g').'"'
  761. endfunction
  762. function! s:glob_dir(path)
  763. return map(filter(split(globpath(a:path, '**'), '\n'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
  764. endfunction
  765. function! s:progress_bar(line, bar, total)
  766. call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
  767. endfunction
  768. function! s:compare_git_uri(a, b)
  769. let a = substitute(a:a, 'git:@', '', '')
  770. let b = substitute(a:b, 'git:@', '', '')
  771. return a ==# b
  772. endfunction
  773. function! s:format_message(ok, name, message)
  774. if a:ok
  775. return [printf('- %s: %s', a:name, s:lastline(a:message))]
  776. else
  777. let lines = map(split(a:message, '\n'), '" ".v:val')
  778. return extend([printf('x %s:', a:name)], lines)
  779. endif
  780. endfunction
  781. function! s:system(cmd)
  782. return system(s:is_win ? '('.a:cmd.')' : a:cmd)
  783. endfunction
  784. function! s:system_chomp(str)
  785. let ret = s:system(a:str)
  786. return v:shell_error ? '' : substitute(ret, '\n$', '', '')
  787. endfunction
  788. function! s:git_valid(spec, check_branch, cd)
  789. let ret = 1
  790. let msg = 'OK'
  791. if isdirectory(a:spec.dir)
  792. if a:cd | execute "cd " . s:esc(a:spec.dir) | endif
  793. let result = split(s:system("git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url"), '\n')
  794. let remote = result[-1]
  795. if v:shell_error
  796. let msg = join([remote, "PlugClean required."], "\n")
  797. let ret = 0
  798. elseif !s:compare_git_uri(remote, a:spec.uri)
  799. let msg = join(['Invalid URI: '.remote,
  800. \ 'Expected: '.a:spec.uri,
  801. \ "PlugClean required."], "\n")
  802. let ret = 0
  803. elseif a:check_branch
  804. let branch = result[0]
  805. if a:spec.branch !=# branch
  806. let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1')
  807. if a:spec.branch !=# tag
  808. let msg = printf('Invalid branch/tag: %s (expected: %s). Try PlugUpdate.',
  809. \ (empty(tag) ? branch : tag), a:spec.branch)
  810. let ret = 0
  811. endif
  812. endif
  813. endif
  814. if a:cd | cd - | endif
  815. else
  816. let msg = 'Not found'
  817. let ret = 0
  818. endif
  819. return [ret, msg]
  820. endfunction
  821. function! s:clean(force)
  822. call s:prepare()
  823. call append(0, 'Searching for unused plugins in '.g:plug_home)
  824. call append(1, '')
  825. " List of valid directories
  826. let dirs = []
  827. let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
  828. let [cnt, total] = [0, len(managed)]
  829. for spec in values(managed)
  830. if s:git_valid(spec, 0, 1)[0]
  831. call add(dirs, spec.dir)
  832. endif
  833. let cnt += 1
  834. call s:progress_bar(2, repeat('=', cnt), total)
  835. normal! 2G
  836. redraw
  837. endfor
  838. let allowed = {}
  839. for dir in dirs
  840. let allowed[dir] = 1
  841. for child in s:glob_dir(dir)
  842. let allowed[child] = 1
  843. endfor
  844. endfor
  845. let todo = []
  846. let found = sort(s:glob_dir(g:plug_home))
  847. while !empty(found)
  848. let f = remove(found, 0)
  849. if !has_key(allowed, f) && isdirectory(f)
  850. call add(todo, f)
  851. call append(line('$'), '- ' . f)
  852. let found = filter(found, 'stridx(v:val, f) != 0')
  853. end
  854. endwhile
  855. normal! G
  856. redraw
  857. if empty(todo)
  858. call append(line('$'), 'Already clean.')
  859. else
  860. call inputsave()
  861. let yes = a:force || (input("Proceed? (Y/N) ") =~? '^y')
  862. call inputrestore()
  863. if yes
  864. for dir in todo
  865. if isdirectory(dir)
  866. call system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(dir))
  867. endif
  868. endfor
  869. call append(line('$'), 'Removed.')
  870. else
  871. call append(line('$'), 'Cancelled.')
  872. endif
  873. endif
  874. normal! G
  875. endfunction
  876. function! s:upgrade()
  877. if executable('curl')
  878. let mee = s:shellesc(s:me)
  879. let new = s:shellesc(s:me . '.new')
  880. echo "Downloading ". s:plug_source
  881. redraw
  882. let mv = s:is_win ? 'move /Y' : 'mv -f'
  883. let cp = s:is_win ? 'copy /Y' : 'cp -f'
  884. call system(printf(
  885. \ "curl -fLo %s %s && ".cp." %s %s.old && ".mv." %s %s",
  886. \ new, s:plug_source, mee, mee, new, mee))
  887. if v:shell_error == 0
  888. unlet g:loaded_plug
  889. echo "Downloaded ". s:plug_source
  890. return 1
  891. else
  892. echoerr "Error upgrading vim-plug"
  893. return 0
  894. endif
  895. elseif has('ruby')
  896. echo "Downloading ". s:plug_source
  897. ruby << EOF
  898. require 'open-uri'
  899. require 'fileutils'
  900. me = VIM::evaluate('s:me')
  901. old = me + '.old'
  902. new = me + '.new'
  903. File.open(new, 'w') do |f|
  904. f << open(VIM::evaluate('s:plug_source')).read
  905. end
  906. FileUtils.cp me, old
  907. File.rename new, me
  908. EOF
  909. unlet g:loaded_plug
  910. echo "Downloaded ". s:plug_source
  911. return 1
  912. else
  913. echoerr "curl executable or ruby support not found"
  914. return 0
  915. endif
  916. endfunction
  917. function! s:status()
  918. call s:prepare()
  919. call append(0, 'Checking plugins')
  920. call append(1, '')
  921. let ecnt = 0
  922. let [cnt, total] = [0, len(g:plugs)]
  923. for [name, spec] in items(g:plugs)
  924. if has_key(spec, 'uri')
  925. if isdirectory(spec.dir)
  926. let [valid, msg] = s:git_valid(spec, 1, 1)
  927. else
  928. let [valid, msg] = [0, 'Not found. Try PlugInstall.']
  929. endif
  930. else
  931. if isdirectory(spec.dir)
  932. let [valid, msg] = [1, 'OK']
  933. else
  934. let [valid, msg] = [0, 'Not found.']
  935. endif
  936. endif
  937. let cnt += 1
  938. let ecnt += !valid
  939. call s:progress_bar(2, repeat('=', cnt), total)
  940. call append(3, s:format_message(valid, name, msg))
  941. normal! 2G
  942. redraw
  943. endfor
  944. call setline(1, 'Finished. '.ecnt.' error(s).')
  945. normal! gg
  946. endfunction
  947. function! s:is_preview_window_open()
  948. silent! wincmd P
  949. if &previewwindow
  950. wincmd p
  951. return 1
  952. endif
  953. return 0
  954. endfunction
  955. function! s:preview_commit()
  956. if b:plug_preview < 0
  957. let b:plug_preview = !s:is_preview_window_open()
  958. endif
  959. let sha = matchstr(getline('.'), '\(^ \)\@<=[0-9a-z]\{7}')
  960. if !empty(sha)
  961. let lnum = line('.')
  962. while lnum > 1
  963. let lnum -= 1
  964. let line = getline(lnum)
  965. let name = matchstr(line, '\(^- \)\@<=[^:]\+')
  966. if !empty(name)
  967. let dir = g:plugs[name].dir
  968. if isdirectory(dir)
  969. execute 'cd '.s:esc(dir)
  970. execute 'pedit '.sha
  971. wincmd P
  972. setlocal filetype=git buftype=nofile nobuflisted
  973. execute 'silent read !git show '.sha
  974. normal! ggdd
  975. wincmd p
  976. cd -
  977. endif
  978. break
  979. endif
  980. endwhile
  981. endif
  982. endfunction
  983. function! s:section(flags)
  984. call search('\(^- \)\@<=.', a:flags)
  985. endfunction
  986. function! s:diff()
  987. call s:prepare()
  988. call append(0, 'Collecting updated changes ...')
  989. normal! gg
  990. redraw
  991. let cnt = 0
  992. for [k, v] in items(g:plugs)
  993. if !isdirectory(v.dir) || !s:is_managed(k)
  994. continue
  995. endif
  996. execute 'cd '.s:esc(v.dir)
  997. let diff = system('git log --pretty=format:"%h %s (%cr)" "HEAD@{0}...HEAD@{1}"')
  998. if !v:shell_error && !empty(diff)
  999. call append(1, '')
  1000. call append(2, '- '.k.':')
  1001. call append(3, map(split(diff, '\n'), '" ". v:val'))
  1002. let cnt += 1
  1003. normal! gg
  1004. redraw
  1005. endif
  1006. cd -
  1007. endfor
  1008. call setline(1, cnt == 0 ? 'No updates.' : 'Last update:')
  1009. nnoremap <silent> <buffer> <cr> :silent! call <SID>preview_commit()<cr>
  1010. normal! gg
  1011. endfunction
  1012. let s:first_rtp = s:esc(get(split(&rtp, ','), 0, ''))
  1013. let s:last_rtp = s:esc(get(split(&rtp, ','), -1, ''))
  1014. let &cpo = s:cpo_save
  1015. unlet s:cpo_save