plug.vim 32 KB

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