plug.vim 32 KB

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