plug.vim 32 KB

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