plug.vim 32 KB

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